Merge "Migrate Bubbles to wm-shell (1/n)"
diff --git a/Android.bp b/Android.bp
index c6f9362..5953007 100644
--- a/Android.bp
+++ b/Android.bp
@@ -547,6 +547,7 @@
exclude_srcs: ["core/java/android/content/pm/AndroidTestBaseUpdater.java"],
aidl: {
generate_get_transaction_name: true,
+ local_include_dirs: ["media/aidl"],
},
dxflags: [
"--core-library",
@@ -583,6 +584,7 @@
// in favor of an API stubs dependency in java_library "framework" below.
"mimemap",
"mediatranscoding_aidl_interface-java",
+ "soundtrigger_middleware-aidl-java",
],
// For backwards compatibility.
stem: "framework",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index ca921ff..c82fee0f 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -83,6 +83,10 @@
merge_annotations_dirs: [
"metalava-manual",
],
+ // TODO(b/169090544): remove below aidl includes.
+ aidl: {
+ local_include_dirs: ["media/aidl"],
+ },
}
droidstubs {
@@ -150,6 +154,10 @@
":current-support-api",
":current-androidx-api",
],
+ // TODO(b/169090544): remove below aidl includes.
+ aidl: {
+ local_include_dirs: ["media/aidl"],
+ },
}
doc_defaults {
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index fc5efc6..7a8d1a1 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -11,6 +11,7 @@
libs/input/
services/core/jni/
services/incremental/
+ tools/
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bb65387..852fcc6 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -51,9 +51,12 @@
":android_icu4j_public_api_files",
"**/package.html",
],
- // TODO(b/147699819): remove below aidl includes.
+ // TODO(b/147699819, b/169090544): remove below aidl includes.
aidl: {
- local_include_dirs: ["telephony/java"],
+ local_include_dirs: [
+ "telephony/java",
+ "media/aidl",
+ ],
},
libs: ["framework-internal-utils"],
installable: false,
@@ -488,29 +491,3 @@
":hwbinder-stubs-docs",
],
}
-
-/////////////////////////////////////////////////////////////////////
-// api/*-current.txt files for use by modules in other directories
-// like the CTS test
-/////////////////////////////////////////////////////////////////////
-
-filegroup {
- name: "frameworks-base-api-current.txt",
- srcs: [
- "api/current.txt",
- ],
-}
-
-filegroup {
- name: "frameworks-base-api-system-current.txt",
- srcs: [
- "api/system-current.txt",
- ],
-}
-
-filegroup {
- name: "frameworks-base-api-system-removed.txt",
- srcs: [
- "api/system-removed.txt",
- ],
-}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index d97b500..54e1860 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -16,19 +16,23 @@
package android.view.autofill;
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
import static org.junit.Assert.assertTrue;
import android.os.Looper;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.PerfTestActivity;
-import android.perftests.utils.SettingsHelper;
import android.perftests.utils.SettingsStateKeeperRule;
import android.provider.Settings;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.rules.RuleChain;
@@ -38,6 +42,8 @@
*/
public abstract class AbstractAutofillPerfTestCase {
+ private static final String TAG = "AbstractAutofillPerfTestCase";
+
@ClassRule
public static final SettingsStateKeeperRule mServiceSettingsKeeper =
new SettingsStateKeeperRule(InstrumentationRegistry.getTargetContext(),
@@ -60,6 +66,27 @@
mLayoutId = layoutId;
}
+ @BeforeClass
+ public static void disableDefaultAugmentedService() {
+ Log.v(TAG, "@BeforeClass: disableDefaultAugmentedService()");
+ setDefaultAugmentedAutofillServiceEnabled(false);
+ }
+
+ @AfterClass
+ public static void enableDefaultAugmentedService() {
+ Log.v(TAG, "@AfterClass: enableDefaultAugmentedService()");
+ setDefaultAugmentedAutofillServiceEnabled(true);
+ }
+
+ /**
+ * Enables / disables the default augmented autofill service.
+ */
+ private static void setDefaultAugmentedAutofillServiceEnabled(boolean enabled) {
+ Log.d(TAG, "setDefaultAugmentedAutofillServiceEnabled(): " + enabled);
+ runShellCommand("cmd autofill set default-augmented-service-enabled 0 %s",
+ Boolean.toString(enabled));
+ }
+
/**
* Prepares the activity so that by the time the test is run it has reference to its fields.
*/
@@ -80,23 +107,4 @@
* Initializes the {@link PerfTestActivity} after it was launched.
*/
protected abstract void onCreate(PerfTestActivity activity);
-
- /**
- * Uses the {@code settings} binary to set the autofill service.
- */
- protected void setService() {
- SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
- SettingsHelper.NAMESPACE_SECURE,
- Settings.Secure.AUTOFILL_SERVICE,
- MyAutofillService.COMPONENT_NAME);
- }
-
- /**
- * Uses the {@code settings} binary to reset the autofill service.
- */
- protected void resetService() {
- SettingsHelper.syncDelete(InstrumentationRegistry.getTargetContext(),
- SettingsHelper.NAMESPACE_SECURE,
- Settings.Secure.AUTOFILL_SERVICE);
- }
}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
index f1f812d..2475d98 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AutofillTestWatcher.java
@@ -18,10 +18,13 @@
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import android.perftests.utils.SettingsHelper;
+import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
@@ -44,19 +47,26 @@
@Override
protected void starting(Description description) {
super.starting(description);
+ final String testName = description.getDisplayName();
+ Log.i(TAG, "Starting " + testName);
enableVerboseLog();
- MyAutofillService.resetStaticState();
- MyAutofillService.setEnabled(true);
+ // Prepare the service before each test.
+ // Disable the current AutofillService.
+ resetAutofillService();
+ // Set MyAutofillService status enable, it can start to accept the calls.
+ enableMyAutofillService();
setServiceWatcher();
}
@Override
protected void finished(Description description) {
super.finished(description);
-
+ final String testName = description.getDisplayName();
+ Log.i(TAG, "Finished " + testName);
restoreLogLevel();
- disableService();
+ // Set MyAutofillService status disable, so the calls are ignored.
+ disableMyAutofillService();
clearServiceWatcher();
}
@@ -67,12 +77,31 @@
}
}
- private void enableService() {
+ /**
+ * Uses the {@code settings} binary to set the autofill service.
+ */
+ void setAutofillService() {
+ SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(),
+ SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE,
+ MyAutofillService.COMPONENT_NAME);
+ }
+
+ /**
+ * Uses the {@code settings} binary to reset the autofill service.
+ */
+ void resetAutofillService() {
+ SettingsHelper.syncDelete(InstrumentationRegistry.getTargetContext(),
+ SettingsHelper.NAMESPACE_SECURE,
+ Settings.Secure.AUTOFILL_SERVICE);
+ }
+
+ private void enableMyAutofillService() {
MyAutofillService.resetStaticState();
MyAutofillService.setEnabled(true);
}
- private void disableService() {
+ private void disableMyAutofillService() {
// Must disable service so calls are ignored in case of errors during the test case;
// otherwise, other tests will fail because these calls are made in the UI thread (as both
// the service, the tests, and the app run in the same process).
@@ -88,7 +117,7 @@
}
private void restoreLogLevel() {
- Log.w(TAG, "restoreLogLevel to " + mOriginalLogLevel);
+ Log.d(TAG, "restoreLogLevel to " + mOriginalLogLevel);
if (!mOriginalLogLevel.equals("verbose")) {
runShellCommand("cmd autofill set log_level %s", mOriginalLogLevel);
}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 99b2590..37b4bde 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -55,7 +55,7 @@
*/
@Test
public void testFocus_noService() throws Throwable {
- resetService();
+ mTestWatcher.resetAutofillService();
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
@@ -73,7 +73,7 @@
@Test
public void testFocus_serviceDoesNotAutofill() throws Throwable {
MyAutofillService.newCannedResponse().reply();
- setService();
+ mTestWatcher.setAutofillService();
// Must first focus in a field to trigger autofill and wait for service response
// outside the loop
@@ -102,7 +102,7 @@
.setUsername(mUsername.getAutofillId(), "user")
.setPassword(mPassword.getAutofillId(), "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
// Callback is used to slow down the calls made to the autofill server so the
// app is not crashed due to binder exhaustion. But the time spent waiting for the callbacks
@@ -157,7 +157,7 @@
.setUsername(mUsername.getAutofillId(), "user")
.setIgnored(mPassword.getAutofillId())
.reply();
- setService();
+ mTestWatcher.setAutofillService();
// Callback is used to slow down the calls made to the autofill server so the
// app is not crashed due to binder exhaustion. But the time spent waiting for the callbacks
@@ -201,7 +201,7 @@
*/
@Test
public void testChange_noService() throws Throwable {
- resetService();
+ mTestWatcher.resetAutofillService();
changeTest(false);
}
@@ -213,7 +213,7 @@
@Test
public void testChange_serviceDoesNotAutofill() throws Throwable {
MyAutofillService.newCannedResponse().reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -227,7 +227,7 @@
.setUsername(mUsername.getAutofillId(), "user")
.setPassword(mPassword.getAutofillId(), "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -242,7 +242,7 @@
.setUsername(mUsername.getAutofillId(), "user")
.setIgnored(mPassword.getAutofillId())
.reply();
- setService();
+ mTestWatcher.setAutofillService();
changeTest(true);
}
@@ -274,7 +274,7 @@
.setUsername(mUsername.getAutofillId(), "user")
.setPassword(mPassword.getAutofillId(), "pass")
.reply();
- setService();
+ mTestWatcher.setAutofillService();
MyAutofillCallback callback = new MyAutofillCallback();
mAfm.registerCallback(callback);
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
index 77c1b85..ddac68b 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/MyAutofillService.java
@@ -126,6 +126,7 @@
onError("ignoring onFillRequest(): response not set", callback);
return;
}
+ // TODO(b/162216576): fix error FillResponse
Dataset.Builder dataset = new Dataset.Builder(newDatasetPresentation("dataset"));
boolean hasData = false;
if (response.mUsername != null) {
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 b638fef..4512d77 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2590,13 +2590,14 @@
// job that runs one of the app's services, as well as verifying that the
// named service properly requires the BIND_JOB_SERVICE permission
private void enforceValidJobRequest(int uid, JobInfo job) {
- final IPackageManager pm = AppGlobals.getPackageManager();
+ final PackageManager pm = getContext()
+ .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
+ .getPackageManager();
final ComponentName service = job.getService();
try {
ServiceInfo si = pm.getServiceInfo(service,
PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.getUserId(uid));
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
if (si == null) {
throw new IllegalArgumentException("No such service " + service);
}
@@ -2608,8 +2609,10 @@
throw new IllegalArgumentException("Scheduled service " + service
+ " does not require android.permission.BIND_JOB_SERVICE permission");
}
- } catch (RemoteException e) {
- // Can't happen; the Package Manager is in this same process
+ } catch (NameNotFoundException e) {
+ throw new IllegalArgumentException(
+ "Tried to schedule job for non-existent package: "
+ + service.getPackageName());
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
index 47ebf32..999c53f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java
@@ -18,16 +18,15 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.AppGlobals;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.net.Uri;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -116,10 +115,15 @@
ServiceInfo si = mServiceInfoCache.get(userId, service);
if (si == null) {
try {
- si = AppGlobals.getPackageManager().getServiceInfo(
- service, PackageManager.MATCH_DIRECT_BOOT_AUTO, userId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
+ // createContextAsUser may potentially be expensive
+ // TODO: cache user context or improve ContextImpl implementation if this becomes
+ // a problem
+ si = mContext.createContextAsUser(UserHandle.of(userId), 0)
+ .getPackageManager()
+ .getServiceInfo(service, PackageManager.MATCH_DIRECT_BOOT_AUTO);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Job exists for non-existent package: " + service.getPackageName());
+ return null;
}
mServiceInfoCache.add(userId, service, si);
}
diff --git a/api/Android.bp b/api/Android.bp
index 54ff82c..490c980 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -1,7 +1,100 @@
+// 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 {
+ default_visibility: ["//visibility:private"],
+}
+
+// *-current.txt files for use by modules in other directories like cts
+filegroup {
+ name: "frameworks-base-api-current.txt",
+ srcs: ["current.txt"],
+ visibility: ["//visibility:public"],
+}
+
+filegroup {
+ name: "frameworks-base-api-system-current.txt",
+ srcs: ["system-current.txt"],
+ visibility: ["//visibility:public"],
+}
+
+filegroup {
+ name: "frameworks-base-api-system-removed.txt",
+ srcs: ["system-removed.txt"],
+ visibility: ["//visibility:public"],
+}
+
genrule {
name: "current-api-xml",
tools: ["metalava"],
srcs: ["current.txt"],
out: ["current.api"],
cmd: "$(location metalava) --no-banner -convert2xmlnostrip $(in) $(out)",
+ visibility: ["//visibility:public"],
+}
+
+genrule {
+ name: "frameworks-base-api-current-merged.txt",
+ srcs: [
+ ":conscrypt.module.public.api{.public.api.txt}",
+ ":framework-graphics{.public.api.txt}",
+ ":framework-media{.public.api.txt}",
+ ":framework-mediaprovider{.public.api.txt}",
+ ":framework-permission{.public.api.txt}",
+ ":framework-sdkextensions{.public.api.txt}",
+ ":framework-statsd{.public.api.txt}",
+ ":framework-tethering{.public.api.txt}",
+ ":framework-wifi{.public.api.txt}",
+ ":non-updatable-current.txt",
+ ],
+ out: ["current.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+}
+
+genrule {
+ name: "frameworks-base-api-system-current-merged.txt",
+ srcs: [
+ ":framework-graphics{.system.api.txt}",
+ ":framework-media{.system.api.txt}",
+ ":framework-mediaprovider{.system.api.txt}",
+ ":framework-permission{.system.api.txt}",
+ ":framework-sdkextensions{.system.api.txt}",
+ ":framework-statsd{.system.api.txt}",
+ ":framework-tethering{.system.api.txt}",
+ ":framework-wifi{.system.api.txt}",
+ ":non-updatable-system-current.txt",
+ ],
+ out: ["system-current.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
+}
+
+genrule {
+ name: "frameworks-base-api-module-lib-current-merged.txt",
+ srcs: [
+ ":framework-graphics{.module-lib.api.txt}",
+ ":framework-media{.module-lib.api.txt}",
+ ":framework-mediaprovider{.module-lib.api.txt}",
+ ":framework-permission{.module-lib.api.txt}",
+ ":framework-sdkextensions{.module-lib.api.txt}",
+ ":framework-statsd{.module-lib.api.txt}",
+ ":framework-tethering{.module-lib.api.txt}",
+ ":framework-wifi{.module-lib.api.txt}",
+ ":non-updatable-module-lib-current.txt",
+ ],
+ out: ["module-lib-current.txt"],
+ tools: ["metalava"],
+ cmd: "$(location metalava) --no-banner --format=v2 $(in) --api $(out)",
}
diff --git a/api/current.txt b/api/current.txt
index 4590325..4b0901a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14292,6 +14292,7 @@
public final class BlurShader extends android.graphics.Shader {
ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+ ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode);
}
public class Camera {
@@ -15195,6 +15196,20 @@
ctor public PaintFlagsDrawFilter(int, int);
}
+ public final class ParcelableColorSpace extends android.graphics.ColorSpace implements android.os.Parcelable {
+ ctor public ParcelableColorSpace(@NonNull android.graphics.ColorSpace);
+ method public int describeContents();
+ method @NonNull public float[] fromXyz(@NonNull float[]);
+ method @NonNull public android.graphics.ColorSpace getColorSpace();
+ method public float getMaxValue(int);
+ method public float getMinValue(int);
+ method public static boolean isParcelable(@NonNull android.graphics.ColorSpace);
+ method public boolean isWideGamut();
+ method @NonNull public float[] toXyz(@NonNull float[]);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.graphics.ParcelableColorSpace> CREATOR;
+ }
+
public class Path {
ctor public Path();
ctor public Path(@Nullable android.graphics.Path);
@@ -15630,6 +15645,7 @@
public enum Shader.TileMode {
enum_constant public static final android.graphics.Shader.TileMode CLAMP;
+ enum_constant public static final android.graphics.Shader.TileMode DECAL;
enum_constant public static final android.graphics.Shader.TileMode MIRROR;
enum_constant public static final android.graphics.Shader.TileMode REPEAT;
}
@@ -16373,7 +16389,9 @@
method @Nullable public android.graphics.fonts.FontVariationAxis[] getAxes();
method @NonNull public java.nio.ByteBuffer getBuffer();
method @Nullable public java.io.File getFile();
+ method public float getGlyphBounds(@IntRange(from=0) int, @NonNull android.graphics.Paint, @Nullable android.graphics.RectF);
method @NonNull public android.os.LocaleList getLocaleList();
+ method public void getMetrics(@NonNull android.graphics.Paint, @Nullable android.graphics.Paint.FontMetrics);
method @NonNull public android.graphics.fonts.FontStyle getStyle();
method @IntRange(from=0) public int getTtcIndex();
}
@@ -16385,6 +16403,7 @@
ctor public Font.Builder(@NonNull android.os.ParcelFileDescriptor, @IntRange(from=0) long, @IntRange(from=0xffffffff) long);
ctor public Font.Builder(@NonNull android.content.res.AssetManager, @NonNull String);
ctor public Font.Builder(@NonNull android.content.res.Resources, int);
+ ctor public Font.Builder(@NonNull android.graphics.fonts.Font);
method @NonNull public android.graphics.fonts.Font build() throws java.io.IOException;
method @NonNull public android.graphics.fonts.Font.Builder setFontVariationSettings(@Nullable String);
method @NonNull public android.graphics.fonts.Font.Builder setFontVariationSettings(@Nullable android.graphics.fonts.FontVariationAxis[]);
@@ -24913,6 +24932,10 @@
method public double getAttributeDouble(@NonNull String, double);
method public int getAttributeInt(@NonNull String, int);
method @Nullable public long[] getAttributeRange(@NonNull String);
+ method public long getDateTime();
+ method public long getDateTimeDigitized();
+ method public long getDateTimeOriginal();
+ method public long getGpsDateTime();
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
method public android.graphics.Bitmap getThumbnailBitmap();
@@ -26283,6 +26306,7 @@
field public static final String KEY_ROTATION = "rotation-degrees";
field public static final String KEY_SAMPLE_RATE = "sample-rate";
field public static final String KEY_SLICE_HEIGHT = "slice-height";
+ field public static final String KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
field public static final String KEY_STRIDE = "stride";
field public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
field public static final String KEY_TILE_HEIGHT = "tile-height";
@@ -32009,9 +32033,15 @@
method public int describeContents();
method public String getFqdn();
method public String getFriendlyName();
+ method @Nullable public long[] getMatchAllOis();
+ method @Nullable public long[] getMatchAnyOis();
+ method @Nullable public String[] getOtherHomePartners();
method public long[] getRoamingConsortiumOis();
method public void setFqdn(String);
method public void setFriendlyName(String);
+ method public void setMatchAllOis(@Nullable long[]);
+ method public void setMatchAnyOis(@Nullable long[]);
+ method public void setOtherHomePartners(@Nullable String[]);
method public void setRoamingConsortiumOis(long[]);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index ab89442..be8ea9c 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -11,7 +11,7 @@
}
public class StatusBarManager {
- method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
}
@@ -66,6 +66,7 @@
}
public final class MediaSessionManager {
+ method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler);
method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index c4f9067..118184d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4399,6 +4399,8 @@
}
public static final class MediaTranscodeManager.TranscodingRequest {
+ method public int getClientPid();
+ method public int getClientUid();
method @NonNull public android.net.Uri getDestinationUri();
method public int getPriority();
method @NonNull public android.net.Uri getSourceUri();
@@ -4409,6 +4411,8 @@
public static final class MediaTranscodeManager.TranscodingRequest.Builder {
ctor public MediaTranscodeManager.TranscodingRequest.Builder();
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
@@ -7424,7 +7428,7 @@
field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
- field public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
+ field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
diff --git a/api/test-current.txt b/api/test-current.txt
index 5082c57..9383152 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -514,7 +514,7 @@
method public void expandNotificationsPanel();
method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
- method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
public static final class StatusBarManager.DisableInfo {
@@ -537,7 +537,8 @@
public final class UiAutomation {
method public void destroy();
- method public android.os.ParcelFileDescriptor[] executeShellCommandRw(String);
+ method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRw(@NonNull String);
+ method @NonNull public android.os.ParcelFileDescriptor[] executeShellCommandRwe(@NonNull String);
method @Deprecated public boolean grantRuntimePermission(String, String, android.os.UserHandle);
method @Deprecated public boolean revokeRuntimePermission(String, String, android.os.UserHandle);
method public void syncInputTransactions();
@@ -997,6 +998,7 @@
method public void setEnableRollback(boolean, int);
method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex();
+ method public void setInstallAsInstantApp(boolean);
method public void setInstallerPackageName(@Nullable String);
method public void setRequestDowngrade(boolean);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged();
@@ -1852,6 +1854,8 @@
}
public static final class MediaTranscodeManager.TranscodingRequest {
+ method public int getClientPid();
+ method public int getClientUid();
method @NonNull public android.net.Uri getDestinationUri();
method public int getPriority();
method @NonNull public android.net.Uri getSourceUri();
@@ -1862,6 +1866,8 @@
public static final class MediaTranscodeManager.TranscodingRequest.Builder {
ctor public MediaTranscodeManager.TranscodingRequest.Builder();
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 9c79612..046145f 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -482,6 +482,8 @@
mFlingerSurface = s;
mTargetInset = -1;
+ projectSceneToWindow();
+
// Register a display event receiver
mDisplayEventReceiver = std::make_unique<DisplayEventReceiver>();
status_t status = mDisplayEventReceiver->initCheck();
@@ -493,6 +495,16 @@
return NO_ERROR;
}
+void BootAnimation::projectSceneToWindow() {
+ glViewport(0, 0, mWidth, mHeight);
+ glScissor(0, 0, mWidth, mHeight);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(0, static_cast<float>(mWidth), 0, static_cast<float>(mHeight), -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
void BootAnimation::resizeSurface(int newWidth, int newHeight) {
// We assume this function is called on the animation thread.
if (newWidth == mWidth && newHeight == mHeight) {
@@ -517,8 +529,8 @@
SLOGE("Can't make the new surface current. Error %d", eglGetError());
return;
}
- glViewport(0, 0, mWidth, mHeight);
- glScissor(0, 0, mWidth, mHeight);
+
+ projectSceneToWindow();
mSurface = surface;
}
@@ -799,6 +811,37 @@
return status;
}
+void BootAnimation::fadeFrame(const int frameLeft, const int frameBottom, const int frameWidth,
+ const int frameHeight, const Animation::Part& part,
+ const int fadedFramesCount) {
+ glEnable(GL_BLEND);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDisable(GL_TEXTURE_2D);
+ // avoid creating a hole due to mixing result alpha with GL_REPLACE texture
+ glBlendFuncSeparateOES(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
+
+ const float alpha = static_cast<float>(fadedFramesCount) / part.framesToFadeCount;
+ glColor4f(part.backgroundColor[0], part.backgroundColor[1], part.backgroundColor[2], alpha);
+
+ const float frameStartX = static_cast<float>(frameLeft);
+ const float frameStartY = static_cast<float>(frameBottom);
+ const float frameEndX = frameStartX + frameWidth;
+ const float frameEndY = frameStartY + frameHeight;
+ const GLfloat frameRect[] = {
+ frameStartX, frameStartY,
+ frameEndX, frameStartY,
+ frameEndX, frameEndY,
+ frameStartX, frameEndY
+ };
+ glVertexPointer(2, GL_FLOAT, 0, frameRect);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_TEXTURE_2D);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisable(GL_BLEND);
+}
+
void BootAnimation::drawText(const char* str, const Font& font, bool bold, int* x, int* y) {
glEnable(GL_BLEND); // Allow us to draw on top of the animation
glBindTexture(GL_TEXTURE_2D, font.texture.name);
@@ -890,23 +933,34 @@
int height = 0;
int count = 0;
int pause = 0;
+ int framesToFadeCount = 0;
char path[ANIM_ENTRY_NAME_MAX];
char color[7] = "000000"; // default to black if unspecified
char clockPos1[TEXT_POS_LEN_MAX + 1] = "";
char clockPos2[TEXT_POS_LEN_MAX + 1] = "";
-
char pathType;
+
+ int nextReadPos;
+
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
// SLOGD("> w=%d, h=%d, fps=%d", width, height, fps);
animation.width = width;
animation.height = height;
animation.fps = fps;
- } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s",
- &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) {
- //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s",
- // pathType, count, pause, path, color, clockPos1, clockPos2);
+ } else if (sscanf(l, "%c %d %d %" STRTO(ANIM_PATH_MAX) "s%n",
+ &pathType, &count, &pause, path, &nextReadPos) >= 4) {
+ if (pathType == 'f') {
+ sscanf(l + nextReadPos, " %d #%6s %16s %16s", &framesToFadeCount, color, clockPos1,
+ clockPos2);
+ } else {
+ sscanf(l + nextReadPos, " #%6s %16s %16s", color, clockPos1, clockPos2);
+ }
+ // SLOGD("> type=%c, count=%d, pause=%d, path=%s, framesToFadeCount=%d, color=%s, "
+ // "clockPos1=%s, clockPos2=%s",
+ // pathType, count, pause, path, framesToFadeCount, color, clockPos1, clockPos2);
Animation::Part part;
part.playUntilComplete = pathType == 'c';
+ part.framesToFadeCount = framesToFadeCount;
part.count = count;
part.pause = pause;
part.path = path;
@@ -925,6 +979,7 @@
// SLOGD("> SYSTEM");
Animation::Part part;
part.playUntilComplete = false;
+ part.framesToFadeCount = 0;
part.count = 1;
part.pause = 0;
part.audioData = nullptr;
@@ -1121,12 +1176,19 @@
return false;
}
+bool BootAnimation::shouldStopPlayingPart(const Animation::Part& part, const int fadedFramesCount) {
+ // stop playing only if it is time to exit and it's a partial part which has been faded out
+ return exitPending() && !part.playUntilComplete && fadedFramesCount >= part.framesToFadeCount;
+}
+
bool BootAnimation::playAnimation(const Animation& animation) {
const size_t pcount = animation.parts.size();
nsecs_t frameDuration = s2ns(1) / animation.fps;
SLOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
elapsedRealtime());
+
+ int fadedFramesCount = 0;
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
@@ -1140,10 +1202,9 @@
continue; //to next part
}
- for (int r=0 ; !part.count || r<part.count ; r++) {
- // Exit any non playuntil complete parts immediately
- if(exitPending() && !part.playUntilComplete)
- break;
+ // process the part not only while the count allows but also if already fading
+ for (int r=0 ; !part.count || r<part.count || fadedFramesCount > 0 ; r++) {
+ if (shouldStopPlayingPart(part, fadedFramesCount)) break;
mCallbacks->playPart(i, part, r);
@@ -1153,7 +1214,9 @@
part.backgroundColor[2],
1.0f);
- for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
+ for (size_t j=0 ; j<fcount ; j++) {
+ if (shouldStopPlayingPart(part, fadedFramesCount)) break;
+
processDisplayEvents();
const int animationX = (mWidth - animation.width) / 2;
@@ -1192,11 +1255,22 @@
}
// specify the y center as ceiling((mHeight - frame.trimHeight) / 2)
// which is equivalent to mHeight - (yc + frame.trimHeight)
- glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight),
- 0, frame.trimWidth, frame.trimHeight);
+ const int frameDrawY = mHeight - (yc + frame.trimHeight);
+ glDrawTexiOES(xc, frameDrawY, 0, frame.trimWidth, frame.trimHeight);
+
+ // if the part hasn't been stopped yet then continue fading if necessary
+ if (exitPending() && part.hasFadingPhase()) {
+ fadeFrame(xc, frameDrawY, frame.trimWidth, frame.trimHeight, part,
+ ++fadedFramesCount);
+ if (fadedFramesCount >= part.framesToFadeCount) {
+ fadedFramesCount = MAX_FADED_FRAMES_COUNT; // no more fading
+ }
+ }
+
if (mClockEnabled && mTimeIsAccurate && validClock(part)) {
drawClock(animation.clockFont, part.clockPosX, part.clockPosY);
}
+
handleViewport(frameDuration);
eglSwapBuffers(mDisplay, mSurface);
@@ -1221,11 +1295,11 @@
usleep(part.pause * ns2us(frameDuration));
- // For infinite parts, we've now played them at least once, so perhaps exit
- if(exitPending() && !part.count && mCurrentInset >= mTargetInset)
- break;
+ if (exitPending() && !part.count && mCurrentInset >= mTargetInset &&
+ !part.hasFadingPhase()) {
+ break; // exit the infinite non-fading part when it has been played at least once
+ }
}
-
}
// Free textures created for looping parts now that the animation is done.
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 9e6e4aa..4699cfe 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -19,6 +19,7 @@
#include <vector>
#include <queue>
+#include <climits>
#include <stdint.h>
#include <sys/types.h>
@@ -43,6 +44,8 @@
class BootAnimation : public Thread, public IBinder::DeathRecipient
{
public:
+ static constexpr int MAX_FADED_FRAMES_COUNT = std::numeric_limits<int>::max();
+
struct Texture {
GLint w;
GLint h;
@@ -82,10 +85,15 @@
String8 trimData;
SortedVector<Frame> frames;
bool playUntilComplete;
+ int framesToFadeCount;
float backgroundColor[3];
uint8_t* audioData;
int audioLength;
Animation* animation;
+
+ bool hasFadingPhase() const {
+ return !playUntilComplete && framesToFadeCount > 0;
+ }
};
int fps;
int width;
@@ -160,6 +168,8 @@
bool movie();
void drawText(const char* str, const Font& font, bool bold, int* x, int* y);
void drawClock(const Font& font, const int xPos, const int yPos);
+ void fadeFrame(int frameLeft, int frameBottom, int frameWidth, int frameHeight,
+ const Animation::Part& part, int fadedFramesCount);
bool validClock(const Animation::Part& part);
Animation* loadAnimation(const String8&);
bool playAnimation(const Animation&);
@@ -172,7 +182,9 @@
EGLConfig getEglConfig(const EGLDisplay&);
ui::Size limitSurfaceSize(int width, int height) const;
void resizeSurface(int newWidth, int newHeight);
+ void projectSceneToWindow();
+ bool shouldStopPlayingPart(const Animation::Part& part, int fadedFramesCount);
void checkExit();
void handleViewport(nsecs_t timestep);
diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md
index 5946515..f9b83c9 100644
--- a/cmds/bootanimation/FORMAT.md
+++ b/cmds/bootanimation/FORMAT.md
@@ -30,14 +30,20 @@
It is followed by a number of rows of the form:
- TYPE COUNT PAUSE PATH [#RGBHEX [CLOCK1 [CLOCK2]]]
+ TYPE COUNT PAUSE PATH [FADE [#RGBHEX [CLOCK1 [CLOCK2]]]]
* **TYPE:** a single char indicating what type of animation segment this is:
+ `p` -- this part will play unless interrupted by the end of the boot
+ `c` -- this part will play to completion, no matter what
+ + `f` -- same as `p` but in addition the specified number of frames is being faded out while
+ continue playing. Only the first interrupted `f` part is faded out, other subsequent `f`
+ parts are skipped
* **COUNT:** how many times to play the animation, or 0 to loop forever until boot is complete
* **PAUSE:** number of FRAMES to delay after this part ends
* **PATH:** directory in which to find the frames for this part (e.g. `part0`)
+ * **FADE:** _(ONLY FOR `f` TYPE)_ number of frames to fade out when interrupted where `0` means
+ _immediately_ which makes `f ... 0` behave like `p` and doesn't count it as a fading
+ part
* **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB`
* **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches):
+ If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 0a09801..4ccc7e6 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -3346,6 +3346,7 @@
optional int32 wallpaper_id_hash = 8;
optional int32 color_preference = 9;
optional android.stats.style.LocationPreference location_preference = 10;
+ optional android.stats.style.DatePreference date_preference = 11;
}
/**
diff --git a/cmds/statsd/src/condition/ConditionTimer.h b/cmds/statsd/src/condition/ConditionTimer.h
index 442bc11..1fbe252 100644
--- a/cmds/statsd/src/condition/ConditionTimer.h
+++ b/cmds/statsd/src/condition/ConditionTimer.h
@@ -36,7 +36,7 @@
public:
explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
if (initCondition) {
- mLastConditionTrueTimestampNs = bucketStartNs;
+ mLastConditionChangeTimestampNs = bucketStartNs;
}
};
@@ -44,21 +44,46 @@
// When a new bucket is created, this value will be reset to 0.
int64_t mTimerNs = 0;
- // Last elapsed real timestamp when condition turned to true
- // When a new bucket is created and the condition is true, then the timestamp is set
- // to be the bucket start timestamp.
- int64_t mLastConditionTrueTimestampNs = 0;
+ // Last elapsed real timestamp when condition changed.
+ int64_t mLastConditionChangeTimestampNs = 0;
bool mCondition = false;
int64_t newBucketStart(int64_t nextBucketStartNs) {
if (mCondition) {
- mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs);
- mLastConditionTrueTimestampNs = nextBucketStartNs;
+ // Normally, the next bucket happens after the last condition
+ // change. In this case, add the time between the condition becoming
+ // true to the next bucket start time.
+ // Otherwise, the next bucket start time is before the last
+ // condition change time, this means that the condition was false at
+ // the bucket boundary before the condition became true, so the
+ // timer should not get updated and the last condition change time
+ // remains as is.
+ if (nextBucketStartNs >= mLastConditionChangeTimestampNs) {
+ mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs);
+ mLastConditionChangeTimestampNs = nextBucketStartNs;
+ }
+ } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) {
+ // The next bucket start time is before the last condition change
+ // time, this means that the condition was true at the bucket
+ // boundary before the condition became false, so adjust the timer
+ // to match how long the condition was true to the bucket boundary.
+ // This means remove the amount the condition stayed true in the
+ // next bucket from the current bucket.
+ mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs);
}
int64_t temp = mTimerNs;
mTimerNs = 0;
+
+ if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) {
+ // The next bucket start time is before the last condition change
+ // time, this means that the condition was true at the bucket
+ // boundary and remained true in the next bucket up to the condition
+ // change to false, so adjust the timer to match how long the
+ // condition stayed true in the next bucket (now the current bucket).
+ mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs;
+ }
return temp;
}
@@ -67,11 +92,10 @@
return;
}
mCondition = newCondition;
- if (newCondition) {
- mLastConditionTrueTimestampNs = timestampNs;
- } else {
- mTimerNs += (timestampNs - mLastConditionTrueTimestampNs);
+ if (newCondition == false) {
+ mTimerNs += (timestampNs - mLastConditionChangeTimestampNs);
}
+ mLastConditionChangeTimestampNs = timestampNs;
}
FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
@@ -80,4 +104,4 @@
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 39ae9a4..3d57cfe 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -301,7 +301,6 @@
protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME,
(long long)(NanoToMillis(dropEvent.dropTimeNs)));
- ;
protoOutput->end(dropEventToken);
}
protoOutput->end(wrapperToken);
@@ -346,8 +345,11 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
- // only write the condition timer value if the metric has a condition.
- if (mConditionTrackerIndex >= 0) {
+ // We only write the condition timer value if the metric has a
+ // condition and/or is sliced by state.
+ // If the metric is sliced by state, the condition timer value is
+ // also sliced by state to reflect time spent in that state.
+ if (mConditionTrackerIndex >= 0 || !mSlicedStateAtoms.empty()) {
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
(long long)bucket.mConditionTrueNs);
}
@@ -454,6 +456,8 @@
// Let condition timer know of new active state.
mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs);
}
void ValueMetricProducer::onConditionChangedLocked(const bool condition,
@@ -476,6 +480,8 @@
invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
mCondition = ConditionState::kUnknown;
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
return;
}
@@ -517,6 +523,29 @@
flushIfNeededLocked(eventTimeNs);
mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+ updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
+}
+
+void ValueMetricProducer::updateCurrentSlicedBucketConditionTimers(bool newCondition,
+ int64_t eventTimeNs) {
+ if (mSlicedStateAtoms.empty()) {
+ return;
+ }
+
+ // Utilize the current state key of each DimensionsInWhat key to determine
+ // which condition timers to update.
+ //
+ // Assumes that the MetricDimensionKey exists in `mCurrentSlicedBucket`.
+ bool inPulledData;
+ for (const auto& [dimensionInWhatKey, dimensionInWhatInfo] : mCurrentBaseInfo) {
+ // If the new condition is true, turn ON the condition timer only if
+ // the DimensionInWhat key was present in the pulled data.
+ inPulledData = dimensionInWhatInfo.hasCurrentState;
+ mCurrentSlicedBucket[MetricDimensionKey(dimensionInWhatKey,
+ dimensionInWhatInfo.currentState)]
+ .conditionTimer.onConditionChanged(newCondition && inPulledData, eventTimeNs);
+ }
}
void ValueMetricProducer::prepareFirstBucketLocked() {
@@ -618,8 +647,8 @@
// 2. A superset of the current mStateChangePrimaryKey
// was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
// then we need to reset the base.
- for (auto& slice : mCurrentSlicedBucket) {
- const auto& whatKey = slice.first.getDimensionKeyInWhat();
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat();
bool presentInPulledData =
mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
@@ -627,6 +656,12 @@
for (auto& baseInfo : it->second.baseInfos) {
baseInfo.hasBase = false;
}
+ // Set to false when DimensionInWhat key is not present in a pull.
+ // Used in onMatchedLogEventInternalLocked() to ensure the condition
+ // timer is turned on the next pull when data is present.
+ it->second.hasCurrentState = false;
+ // Turn OFF condition timer for keys not present in pulled data.
+ currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs);
}
}
mMatchedMetricDimensionKeys.clear();
@@ -789,21 +824,26 @@
return;
}
- DimensionsInWhatInfo& dimensionsInWhatInfo = mCurrentBaseInfo[whatKey];
+ const auto& returnVal =
+ mCurrentBaseInfo.emplace(whatKey, DimensionsInWhatInfo(getUnknownStateKey()));
+ DimensionsInWhatInfo& dimensionsInWhatInfo = returnVal.first->second;
+ const HashableDimensionKey oldStateKey = dimensionsInWhatInfo.currentState;
vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos;
if (baseInfos.size() < mFieldMatchers.size()) {
VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
baseInfos.resize(mFieldMatchers.size());
}
+ // Ensure we turn on the condition timer in the case where dimensions
+ // were missing on a previous pull due to a state change.
+ bool stateChange = oldStateKey != stateKey;
if (!dimensionsInWhatInfo.hasCurrentState) {
- dimensionsInWhatInfo.currentState = getUnknownStateKey();
+ stateChange = true;
dimensionsInWhatInfo.hasCurrentState = true;
}
// We need to get the intervals stored with the previous state key so we can
// close these value intervals.
- const auto oldStateKey = dimensionsInWhatInfo.currentState;
vector<Interval>& intervals =
mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
if (intervals.size() < mFieldMatchers.size()) {
@@ -916,6 +956,17 @@
interval.sampleSize += 1;
}
+ // State change.
+ if (!mSlicedStateAtoms.empty() && stateChange) {
+ // Turn OFF the condition timer for the previous state key.
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]
+ .conditionTimer.onConditionChanged(false, eventTimeNs);
+
+ // Turn ON the condition timer for the new state key.
+ mCurrentSlicedBucket[MetricDimensionKey(whatKey, stateKey)]
+ .conditionTimer.onConditionChanged(true, eventTimeNs);
+ }
+
// Only trigger the tracker if all intervals are correct and we have not skipped the bucket due
// to MULTIPLE_BUCKETS_SKIPPED.
if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) {
@@ -990,12 +1041,18 @@
if (!mCurrentBucketIsSkipped) {
bool bucketHasData = false;
// The current bucket is large enough to keep.
- for (const auto& slice : mCurrentSlicedBucket) {
- PastValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second.intervals);
- bucket.mConditionTrueNs = conditionTrueDuration;
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ PastValueBucket bucket =
+ buildPartialBucket(bucketEndTime, currentValueBucket.intervals);
+ if (!mSlicedStateAtoms.empty()) {
+ bucket.mConditionTrueNs =
+ currentValueBucket.conditionTimer.newBucketStart(bucketEndTime);
+ } else {
+ bucket.mConditionTrueNs = conditionTrueDuration;
+ }
// it will auto create new vector of ValuebucketInfo if the key is not found.
if (bucket.valueIndex.size() > 0) {
- auto& bucketList = mPastBuckets[slice.first];
+ auto& bucketList = mPastBuckets[metricDimensionKey];
bucketList.push_back(bucket);
bucketHasData = true;
}
@@ -1023,11 +1080,18 @@
buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
mSkippedBuckets.emplace_back(bucketInGap);
}
-
appendToFullBucket(eventTimeNs > fullBucketEndTimeNs);
initCurrentSlicedBucket(nextBucketStartTimeNs);
// Update the condition timer again, in case we skipped buckets.
mConditionTimer.newBucketStart(nextBucketStartTimeNs);
+
+ // NOTE: Update the condition timers in `mCurrentSlicedBucket` only when slicing
+ // by state. Otherwise, the "global" condition timer will be used.
+ if (!mSlicedStateAtoms.empty()) {
+ for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+ currentValueBucket.conditionTimer.newBucketStart(nextBucketStartTimeNs);
+ }
+ }
mCurrentBucketNum += numBucketsForward;
}
@@ -1069,6 +1133,17 @@
interval.seenNewData = false;
}
+ if (obsolete && !mSlicedStateAtoms.empty()) {
+ // When slicing by state, only delete the MetricDimensionKey when the
+ // state key in the MetricDimensionKey is not the current state key.
+ const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat();
+ const auto& currentBaseInfoItr = mCurrentBaseInfo.find(dimensionInWhatKey);
+
+ if ((currentBaseInfoItr != mCurrentBaseInfo.end()) &&
+ (it->first.getStateValuesKey() == currentBaseInfoItr->second.currentState)) {
+ obsolete = false;
+ }
+ }
if (obsolete) {
it = mCurrentSlicedBucket.erase(it);
} else {
@@ -1104,7 +1179,7 @@
// Accumulate partial buckets with current value and then send to anomaly tracker.
if (mCurrentFullBucket.size() > 0) {
for (const auto& slice : mCurrentSlicedBucket) {
- if (hitFullBucketGuardRailLocked(slice.first)) {
+ if (hitFullBucketGuardRailLocked(slice.first) || slice.second.intervals.empty()) {
continue;
}
// TODO: fix this when anomaly can accept double values
@@ -1125,7 +1200,7 @@
// Skip aggregating the partial buckets since there's no previous partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
for (auto& tracker : mAnomalyTrackers) {
- if (tracker != nullptr) {
+ if (tracker != nullptr && !slice.second.intervals.empty()) {
// TODO: fix this when anomaly can accept double values
auto& interval = slice.second.intervals[0];
if (interval.hasValue) {
@@ -1139,10 +1214,12 @@
} else {
// Accumulate partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
- // TODO: fix this when anomaly can accept double values
- auto& interval = slice.second.intervals[0];
- if (interval.hasValue) {
- mCurrentFullBucket[slice.first] += interval.value.long_value;
+ if (!slice.second.intervals.empty()) {
+ // TODO: fix this when anomaly can accept double values
+ auto& interval = slice.second.intervals[0];
+ if (interval.hasValue) {
+ mCurrentFullBucket[slice.first] += interval.value.long_value;
+ }
}
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 4b2599b..67de214 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -193,8 +193,14 @@
// Internal state of an ongoing aggregation bucket.
typedef struct CurrentValueBucket {
+ // If the `MetricDimensionKey` state key is the current state key, then
+ // the condition timer will be updated later (e.g. condition/state/active
+ // state change) with the correct condition and time.
+ CurrentValueBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) {}
// Value information for each value field of the metric.
std::vector<Interval> intervals;
+ // Tracks how long the condition is true.
+ ConditionTimer conditionTimer;
} CurrentValueBucket;
// Holds base information for diffing values from one value field.
@@ -206,7 +212,10 @@
} BaseInfo;
// State key and base information for a specific DimensionsInWhat key.
- typedef struct {
+ typedef struct DimensionsInWhatInfo {
+ DimensionsInWhatInfo(const HashableDimensionKey& stateKey)
+ : baseInfos(), currentState(stateKey), hasCurrentState(false) {
+ }
std::vector<BaseInfo> baseInfos;
// Last seen state value(s).
HashableDimensionKey currentState;
@@ -252,6 +261,10 @@
// Reset diff base and mHasGlobalBase
void resetBase();
+ // Updates the condition timers in the current sliced bucket when there is a
+ // condition change or an active state change.
+ void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs);
+
static const size_t kBucketSize = sizeof(PastValueBucket{});
const size_t mDimensionSoftLimit;
@@ -337,6 +350,11 @@
FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket);
+ FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 5dbf16d..2ae5763 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -24,6 +24,8 @@
#include "matchers/EventMatcherWizard.h"
#include "metrics_manager_util.h"
+using google::protobuf::MessageLite;
+
namespace android {
namespace os {
namespace statsd {
@@ -419,16 +421,19 @@
return false;
}
-bool determineEventMetricUpdateStatus(const StatsdConfig& config, const EventMetric& metric,
- const unordered_map<int64_t, int>& oldMetricProducerMap,
- const vector<sp<MetricProducer>>& oldMetricProducers,
- const unordered_map<int64_t, int>& metricToActivationMap,
- const set<int64_t>& replacedMatchers,
- const set<int64_t>& replacedConditions,
- UpdateStatus& updateStatus) {
- int64_t id = metric.id();
+bool determineMetricUpdateStatus(
+ const StatsdConfig& config, const MessageLite& metric, const int64_t metricId,
+ const MetricType metricType, const set<int64_t>& matcherDependencies,
+ const set<int64_t>& conditionDependencies,
+ const ::google::protobuf::RepeatedField<int64_t>& stateDependencies,
+ const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& conditionLinks,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions,
+ const set<int64_t>& replacedStates, UpdateStatus& updateStatus) {
// Check if new metric
- const auto& oldMetricProducerIt = oldMetricProducerMap.find(id);
+ const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
if (oldMetricProducerIt == oldMetricProducerMap.end()) {
updateStatus = UPDATE_NEW;
return true;
@@ -436,44 +441,85 @@
// This is an existing metric, check if it has changed.
uint64_t metricHash;
- if (!getMetricProtoHash(config, metric, id, metricToActivationMap, metricHash)) {
+ if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) {
return false;
}
const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
- if (oldMetricProducer->getMetricType() != METRIC_TYPE_EVENT ||
+ if (oldMetricProducer->getMetricType() != metricType ||
oldMetricProducer->getProtoHash() != metricHash) {
updateStatus = UPDATE_REPLACE;
return true;
}
- // Metric type and definition are the same. Need to check dependencies to see if they changed.
- if (replacedMatchers.find(metric.what()) != replacedMatchers.end()) {
+ // Take intersections of the matchers/predicates/states that the metric
+ // depends on with those that have been replaced. If a metric depends on any
+ // replaced component, it too must be replaced.
+ set<int64_t> intersection;
+ set_intersection(matcherDependencies.begin(), matcherDependencies.end(),
+ replacedMatchers.begin(), replacedMatchers.end(),
+ inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+ set_intersection(conditionDependencies.begin(), conditionDependencies.end(),
+ replacedConditions.begin(), replacedConditions.end(),
+ inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+ set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(),
+ replacedStates.end(), inserter(intersection, intersection.begin()));
+ if (intersection.size() > 0) {
updateStatus = UPDATE_REPLACE;
return true;
}
- if (metric.has_condition()) {
- if (replacedConditions.find(metric.condition()) != replacedConditions.end()) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
- }
-
- if (metricActivationDepsChange(config, metricToActivationMap, id, replacedMatchers)) {
- updateStatus = UPDATE_REPLACE;
- return true;
- }
-
- for (const auto& metricConditionLink : metric.links()) {
+ for (const auto& metricConditionLink : conditionLinks) {
if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) {
updateStatus = UPDATE_REPLACE;
return true;
}
}
+
+ if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) {
+ updateStatus = UPDATE_REPLACE;
+ return true;
+ }
+
updateStatus = UPDATE_PRESERVE;
return true;
}
+bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
+ const unordered_map<int64_t, int>& oldMetricProducerMap,
+ const vector<sp<MetricProducer>>& oldMetricProducers,
+ const unordered_map<int64_t, int>& metricToActivationMap,
+ const set<int64_t>& replacedMatchers,
+ const set<int64_t>& replacedConditions,
+ const set<int64_t>& replacedStates,
+ vector<UpdateStatus>& metricsToUpdate) {
+ int metricIndex = 0;
+ for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
+ const EventMetric& metric = config.event_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()},
+ conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
+ metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
+ // TODO: determine update status for count, gauge, value, duration metrics.
+ return true;
+}
+
bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
@@ -518,22 +564,16 @@
}
vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN);
+ if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap, replacedMatchers,
+ replacedConditions, replacedStates, metricsToUpdate)) {
+ return false;
+ }
+
+ // Now, perform the update. Must iterate the metric types in the same order
int metricIndex = 0;
for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
newMetricProducerMap[config.event_metric(i).id()] = metricIndex;
- if (!determineEventMetricUpdateStatus(config, config.event_metric(i), oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions,
- metricsToUpdate[metricIndex])) {
- return false;
- }
- }
-
- // TODO: determine update status for count, gauge, value, duration metrics.
-
- // Now, perform the update. Must iterate the metric types in the same order
- metricIndex = 0;
- for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
const EventMetric& metric = config.event_metric(i);
switch (metricsToUpdate[metricIndex]) {
case UPDATE_PRESERVE: {
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
index 1cd0ce5..34d7e9c 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -125,23 +125,25 @@
std::vector<ConditionState>& conditionCache,
std::set<int64_t>& replacedConditions);
-// Function to determine if an event metric needs to be updated. Populates updateStatus.
+// Function to determine the update status (preserve/replace/new) of all metrics in the config.
// [config]: the input StatsdConfig
-// [metric]: the current metric to be updated
// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager
// [oldMetricProducers]: stores the existing MetricProducers
-// [metricToActivationMap]: map from metric id to metric activation index.
-// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced
+// [metricToActivationMap]: map from metric id to metric activation index
+// [replacedMatchers]: set of replaced matcher ids. metrics using these matchers must be replaced
+// [replacedConditions]: set of replaced conditions. metrics using these conditions must be replaced
+// [replacedStates]: set of replaced state ids. metrics using these states must be replaced
// output:
-// [updateStatus]: update status for the metric. Will be changed from UPDATE_UNKNOWN after this call
+// [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN
// Returns whether the function was successful or not.
-bool determineEventMetricUpdateStatus(const StatsdConfig& config, const EventMetric& metric,
+bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
const unordered_map<int64_t, int>& oldMetricProducerMap,
const vector<sp<MetricProducer>>& oldMetricProducers,
const unordered_map<int64_t, int>& metricToActivationMap,
const set<int64_t>& replacedMatchers,
const set<int64_t>& replacedConditions,
- UpdateStatus& updateStatus);
+ const set<int64_t>& replacedStates,
+ vector<UpdateStatus>& metricsToUpdate);
// Update MetricProducers.
// input:
diff --git a/cmds/statsd/tests/condition/ConditionTimer_test.cpp b/cmds/statsd/tests/condition/ConditionTimer_test.cpp
index ea02cd3..46dc9a9 100644
--- a/cmds/statsd/tests/condition/ConditionTimer_test.cpp
+++ b/cmds/statsd/tests/condition/ConditionTimer_test.cpp
@@ -35,11 +35,11 @@
EXPECT_EQ(0, timer.mTimerNs);
timer.onConditionChanged(true, ct_start_time + 5);
- EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time + 5, timer.mLastConditionChangeTimestampNs);
EXPECT_EQ(true, timer.mCondition);
EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100));
- EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time + 100, timer.mLastConditionChangeTimestampNs);
EXPECT_EQ(true, timer.mCondition);
}
@@ -51,7 +51,7 @@
EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time));
EXPECT_EQ(true, timer.mCondition);
EXPECT_EQ(0, timer.mTimerNs);
- EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs);
+ EXPECT_EQ(ct_start_time, timer.mLastConditionChangeTimestampNs);
timer.onConditionChanged(false, ct_start_time + 5);
EXPECT_EQ(5, timer.mTimerNs);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index b166cc1..6cf4192 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -93,6 +93,13 @@
}
}
+static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition,
+ int64_t timerNs, int64_t lastConditionTrueTimestampNs) {
+ EXPECT_EQ(condition, conditionTimer.mCondition);
+ EXPECT_EQ(timerNs, conditionTimer.mTimerNs);
+ EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs);
+}
+
} // anonymous namespace
class ValueMetricProducerTestHelper {
@@ -3967,33 +3974,37 @@
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
return true;
}))
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9));
return true;
}))
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
return true;
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
return true;
}));
@@ -4025,12 +4036,13 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after screen state change kStateUnknown->ON.
auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4040,34 +4052,13 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for dimension, state key {{}, kStateUnknown}
- EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
- ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
- it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(2, it->second.intervals[0].value.long_value);
-
- // Bucket status after screen state change ON->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
- android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
- StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {}
- it = valueProducer->mCurrentSlicedBucket.begin();
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
- itBase->second.currentState.getValues()[0].mValue.int_value);
// Value for dimension, state key {{}, ON}
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
- EXPECT_TRUE(it->second.intervals[0].hasValue);
- EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ EXPECT_EQ(0, it->second.intervals.size());
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4076,9 +4067,52 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
+
+ // Bucket status after screen state change ON->OFF.
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+ android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+ StateManager::getInstance().onLogEvent(*screenEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(0, it->second.intervals.size());
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for dimension, state key {{}, ON}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for dimension, state key {{}, kStateUnknown}
+ it++;
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_TRUE(it->second.intervals[0].hasValue);
+ EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change OFF->ON.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4098,6 +4132,8 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(12, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, ON}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4106,6 +4142,8 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4114,37 +4152,46 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucketStartTimeNs + 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(3, report.value_metrics().data_size());
+ // {{}, kStateUnknown}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, ON}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, OFF}
data = report.value_metrics().data(2);
ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
}
/*
@@ -4169,9 +4216,10 @@
// Screen state change to ON.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
return true;
}))
// Screen state change to VR has no pull because it is in the same
@@ -4183,17 +4231,19 @@
// Screen state change to OFF.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
return true;
}))
// Dump report requested.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
return true;
}));
@@ -4236,12 +4286,13 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after screen state change kStateUnknown->ON.
auto screenEvent = CreateScreenStateChangedEvent(
- bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4251,20 +4302,29 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.long_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change ON->VR.
// Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_VR);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4274,20 +4334,29 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change VR->ON.
// Both ON and VR are in the same state group, so the base should not change.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_ON);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4297,19 +4366,28 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, ON GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOnGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Bucket status after screen state change VR->OFF.
- screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+ screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
StateManager::getInstance().onLogEvent(*screenEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {}
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4319,13 +4397,22 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(screenOffGroup.group_id(),
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, OFF GROUP}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(screenOffGroup.group_id(),
+ it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, ON GROUP}
+ it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(screenOnGroup.group_id(),
it->first.getStateValuesKey().getValues()[0].mValue.long_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(16, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 15 * NS_PER_SEC);
// Value for dimension, state key {{}, kStateUnknown}
it++;
EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4334,37 +4421,46 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+ bucketStartTimeNs + 5 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ valueProducer->onDumpReport(bucketStartTimeNs + 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(3, report.value_metrics().data_size());
+ // {{}, kStateUnknown}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, ON GROUP}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {{}, OFF GROUP}
data = report.value_metrics().data(2);
ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_group_id());
EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
}
/*
@@ -4386,6 +4482,35 @@
auto fieldsInState = stateLink->mutable_fields_in_state();
*fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+ /*
+ NOTE: "1" denotes uid 1 and "2" denotes uid 2.
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+
+ (kStateUnknown)
+ 1
+ |-------------|
+ 20
+
+ 2
+ |----------------------------|
+ 40
+
+ (FOREGROUND)
+ 1 1
+ |----------------------------|-------------| |------|
+ 40 20 10
+
+
+ (BACKGROUND)
+ 1
+ |------------|
+ 20
+ 2
+ |-------------|---------------------------------|
+ 20 50
+ */
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
// ValueMetricProducer initialized.
@@ -4400,64 +4525,64 @@
// Uid 1 process state change from kStateUnknown -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 6));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 2 /*uid*/, 8));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 8));
return true;
}))
// Uid 2 process state change from kStateUnknown -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid*/, 9));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 1 /*uid*/, 12));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 12));
return true;
}))
// Uid 1 process state change from Foreground -> Background
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 13));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 2 /*uid*/, 11));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 11));
return true;
}))
// Uid 1 process state change from Background -> Foreground
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 17));
// This event should be skipped.
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 2 /*uid */, 15));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid */, 15));
return true;
}))
// Dump report pull.
.WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
vector<std::shared_ptr<LogEvent>>* data) {
- EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
data->clear();
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20));
- data->push_back(
- CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 1 /*uid*/, 21));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+ 2 /*uid*/, 20));
+ data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+ 1 /*uid*/, 21));
return true;
}));
@@ -4489,6 +4614,7 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Base for dimension key {uid 2}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4505,12 +4631,14 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after uid 1 process state change kStateUnknown -> Foreground.
- auto uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
// Base for dimension key {uid 1}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4528,8 +4656,18 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+ // Value for key {uid 1, FOREGROUND}.
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {uid 2}
+ // Base for dimension key {uid 2}.
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
@@ -4538,22 +4676,42 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {uid 2, kStateUnknown}
+ // Value for key {uid 2, kStateUnknown}.
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
// Bucket status after uid 2 process state change kStateUnknown -> Background.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucketStartTimeNs + 40, 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
- // Base for dimension key {uid 1}.
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {uid 2}.
it = valueProducer->mCurrentSlicedBucket.begin();
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+ EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {uid 2, BACKGROUND}.
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
EXPECT_TRUE(itBase->second.hasCurrentState);
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
@@ -4563,26 +4721,33 @@
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
- // Base for dimension key {uid 2}
+ // Value for key {uid 1, FOREGROUND}.
it++;
- itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
- EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
- EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
- EXPECT_TRUE(itBase->second.hasCurrentState);
- ASSERT_EQ(1, itBase->second.currentState.getValues().size());
- EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
- itBase->second.currentState.getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 2, kStateUnknown}
+ it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
- EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 40 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4612,6 +4777,8 @@
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Base for dimension key {uid 1}
it++;
@@ -4629,6 +4796,8 @@
EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Value for key {uid 1, FOREGROUND}
it++;
@@ -4638,6 +4807,8 @@
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Value for key {uid 2, kStateUnknown}
it++;
@@ -4647,13 +4818,16 @@
EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+ EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
// Bucket status after uid 1 process state change from Foreground -> Background.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucket2StartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
- ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
// Base for dimension key {uid 2}.
@@ -4672,6 +4846,8 @@
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
// Base for dimension key {uid 1}
it++;
itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
@@ -4688,6 +4864,17 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {uid 1, BACKGROUND}
+ it++;
+ ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 1, FOREGROUND}
it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4697,6 +4884,9 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 20 * NS_PER_SEC);
+
// Value for key {uid 2, kStateUnknown}
it++;
ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4705,10 +4895,12 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
// Bucket status after uid 1 process state change Background->Foreground.
- uidProcessEvent = CreateUidProcessStateChangedEvent(
- bucket2StartTimeNs + 40, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
StateManager::getInstance().onLogEvent(*uidProcessEvent);
ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
@@ -4729,6 +4921,7 @@
EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
// Base for dimension key {uid 1}
it++;
@@ -4746,6 +4939,7 @@
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
// Value for key {uid 1, BACKGROUND}
it++;
@@ -4756,6 +4950,8 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 40 * NS_PER_SEC);
// Value for key {uid 1, FOREGROUND}
it++;
@@ -4766,6 +4962,8 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucket2StartTimeNs + 40 * NS_PER_SEC);
// Value for key {uid 2, kStateUnknown}
it++;
@@ -4774,17 +4972,20 @@
ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
// Start dump report and check output.
ProtoOutputStream output;
std::set<string> strSet;
- valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true,
- NO_TIME_CONSTRAINTS, &strSet, &output);
+ 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(5, report.value_metrics().data_size());
+ // {uid 1, BACKGROUND}
auto data = report.value_metrics().data(0);
ASSERT_EQ(1, data.bucket_info_size());
EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
@@ -4792,14 +4993,18 @@
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
data.slice_by_state(0).value());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 2, kStateUnknown}
data = report.value_metrics().data(1);
ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 1, FOREGROUND}
data = report.value_metrics().data(2);
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4808,14 +5013,19 @@
ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size());
EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+ // {uid 1, kStateUnknown}
data = report.value_metrics().data(3);
ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size());
EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long());
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ // {uid 2, BACKGROUND}
data = report.value_metrics().data(4);
EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4824,6 +5034,1630 @@
ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size());
EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long());
EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a state change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+ x (kStateUnknown)
+ |-----------|
+ 10
+
+ x x (ON)
+ |---------------------| |-----------|
+ 20 10
+
+ - (OFF)
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF but data for dimension key {} is not present
+ // in the pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ 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 metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension key {}
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Base for dimension key {}
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode OFF event which is not present
+ // in the pulled data.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_FALSE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode ON event.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 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());
+
+ // {{}, kStateUnknown}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{}, ON}
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test for metric that slices by state when data is not present in pulled data
+ * during an event and then a flush occurs for the current bucket. With the new
+ * condition timer behavior, a "new" MetricDimensionKey is inserted into
+ * `mCurrentSlicedBucket` before intervals are closed/added to that new
+ * MetricDimensionKey.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+ - (kStateUnknown)
+
+ - (ON)
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized but data for dimension key {} is not present
+ // in the pulled data..
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ return true;
+ }))
+ // Battery saver mode state changed to ON but data for dimension key {} is not present
+ // in the pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ 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 metric initialized.
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+
+ // Bucket status after battery saver mode ON event which is not present
+ // in the pulled data.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 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(0, report.value_metrics().data_size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+}
+
+TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------|---------------------------|--
+ x (kStateUnknown)
+ |-----|
+ 10
+ x x (ON)
+ |-----| |-----------|
+ 10 20
+ x (OFF)
+ |------------------------|
+ 40
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // ValueMetricProducer initialized.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+ return true;
+ }))
+ // Battery saver mode state changed to OFF.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7));
+ return true;
+ }))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10));
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+ 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 metric initialized.
+ ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for dimension, state key {{}, kStateUnknown}
+ EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+ // Bucket status after battery saver mode ON event.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode OFF event.
+ unique_ptr<LogEvent> batterySaverOffEvent =
+ CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{}, ON}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Bucket status after battery saver mode ON event.
+ batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+ bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, ON}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> 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(3, report.value_metrics().data_size());
+
+ // {{}, kStateUnknown}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{}, ON}
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{}, OFF}
+ data = report.value_metrics().data(2);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a condition change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
+ "BATTERY_SAVER_MODE_STATE");
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ /*
+ NOTE: "-" means that the data was not present in the pulled data.
+
+ bucket # 1
+ 10 20 30 40 50 60 (seconds)
+ |-------------------------------------------------------|--
+
+ T F T (Condition)
+ x (ON)
+ |----------------------| -
+ 20
+ */
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Battery saver mode state changed to ON.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(
+ CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3));
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ 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 true but data for dimension key {} is not present in the
+ // pulled data.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateRepeatedValueLogEvent(
+ tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
+ ConditionState::kTrue);
+ 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.
+ unique_ptr<LogEvent> batterySaverOnEvent =
+ CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+ StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after condition change to false.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_TRUE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC);
+ // Base for dimension key {}
+ ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_FALSE(itBase->second.hasCurrentState);
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, ON}
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{}, -1}
+ it++;
+ 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);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> strSet;
+ valueProducer->onDumpReport(bucketStartTimeNs + 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(1, report.value_metrics().data_size());
+
+ // {{}, ON}
+ ValueMetricData data = report.value_metrics().data(0);
+ EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state with a primary field,
+ * condition, and has multiple dimensions.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
+ // Set up ValueMetricProducer.
+ ValueMetric metric =
+ ValueMetricProducerTestHelper::createMetricWithConditionAndState("UID_PROCESS_STATE");
+ metric.mutable_dimensions_in_what()->set_field(tagId);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+ metric.mutable_dimensions_in_what()->add_child()->set_field(3);
+
+ MetricStateLink* stateLink = metric.add_state_link();
+ stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+ auto fieldsInWhat = stateLink->mutable_fields_in_what();
+ *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
+ auto fieldsInState = stateLink->mutable_fields_in_state();
+ *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+ /*
+ bucket # 1 bucket # 2
+ 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
+ |------------------------------------------|---------------------------------|--
+
+ T F T (Condition)
+ (FOREGROUND)
+ x {1, 14}
+ |------|
+ 10
+
+ x {1, 16}
+ |------|
+ 10
+ x {2, 8}
+ |-------------|
+ 20
+
+ (BACKGROUND)
+ x {1, 14}
+ |-------------| |----------|---------------------------------|
+ 20 15 50
+
+ x {1, 16}
+ |-------------| |----------|---------------------------------|
+ 20 15 50
+
+ x {2, 8}
+ |----------| |----------|-------------------|
+ 15 15 30
+ */
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+ // Uid 1 process state change from kStateUnknown -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 1 /*uid*/, 3, 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 1 /*uid*/, 3, 16 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+ 2 /*uid*/, 5, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 1 process state change from Foreground -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 14 /*tag*/));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 1 /*uid*/, 5, 16 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+ 2 /*uid*/, 7, 8 /*tag*/));
+
+ return true;
+ }))
+ // Uid 2 process state change from kStateUnknown -> Background
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 2 /*uid*/, 9, 8 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 9, 14 /* tag */));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+ 1 /*uid*/, 9, 16 /* tag */));
+
+ return true;
+ }))
+ // Condition changed to false.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 11, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 1 /*uid*/, 11, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+ 2 /*uid*/, 11, 8 /*tag*/));
+
+ return true;
+ }))
+ // Condition changed to true.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 1 /*uid*/, 13, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 1 /*uid*/, 13, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+ 2 /*uid*/, 13, 8 /*tag*/));
+ return true;
+ }))
+ // Uid 2 process state change from Background -> Foreground
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
+
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
+ // This event should be skipped.
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
+
+ return true;
+ }))
+ // Dump report pull.
+ .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+ data->clear();
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */));
+ data->push_back(CreateThreeValueLogEvent(
+ tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/));
+ return true;
+ }));
+
+ StateManager::getInstance().clear();
+ sp<ValueMetricProducer> valueProducer =
+ ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+ pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue);
+ EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+ // Set up StateManager and check that StateTrackers are initialized.
+ 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));
+
+ // Condition is true.
+ // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
+ auto uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 1, tag 16}.
+ auto it = valueProducer->mCurrentSlicedBucket.begin();
+ auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after uid 1 process state change Foreground -> Background.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 1, tag 16}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket status after uid 2 process state change kStateUnknown -> Background.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 25 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 1 status after condition change to false.
+ // All condition timers should be turned off.
+ valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 15 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 40 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 1 status after condition change to true.
+ valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC);
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 15 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+ bucketStartTimeNs + 45 * NS_PER_SEC);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Pull at end of first bucket.
+ vector<shared_ptr<LogEvent>> allData;
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
+ allData.push_back(
+ CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/));
+ valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+ // Buckets flushed after end of first bucket.
+ // All condition timers' behavior should rollover to bucket 2.
+ ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(5UL, valueProducer->mPastBuckets.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(30 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+ EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Bucket 2 status after uid 2 process state change Background->Foreground.
+ uidProcessEvent =
+ CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
+ android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+ StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+ ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size());
+ ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+ // Base for dimension {uid 2, tag 8}.
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 2, uid 8}, FOREGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, BACKGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+ bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+ // Value for key {{uid 2, uid 8}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Base for dimension {uid 1, tag 16}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, uid 16}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Base for dimension key {uid 1, tag 14}.
+ it++;
+ itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+ ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{uid 1, tag 14}, BACKGROUND}.
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+ // Value for key {{uid 1, uid 16}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 16}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Value for key {{uid 1, tag 14}, FOREGROUND}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+ // Value for key {{uid 1, tag 14}, kStateUnknown}.
+ it++;
+ ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+ EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+ EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+ ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+ EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+ it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+ // Start dump report and check output.
+ ProtoOutputStream output;
+ std::set<string> 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(6, report.value_metrics().data_size());
+
+ // {{uid 1, tag 14}, FOREGROUND}.
+ auto data = report.value_metrics().data(0);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 1, tag 16}, BACKGROUND}.
+ data = report.value_metrics().data(1);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{uid 1, tag 14}, BACKGROUND}.
+ data = report.value_metrics().data(2);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+ // {{uid 1, tag 16}, FOREGROUND}.
+ data = report.value_metrics().data(3);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 2, tag 8}, FOREGROUND}.
+ data = report.value_metrics().data(4);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(1, data.bucket_info_size());
+ EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+ // {{uid 2, tag 8}, BACKGROUND}.
+ data = report.value_metrics().data(5);
+ EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+ EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+ data.slice_by_state(0).value());
+ ASSERT_EQ(2, data.bucket_info_size());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
@@ -4894,15 +6728,23 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::ON,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, -1}
- ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+ // Value for key {{}, ON}
+ ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator 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);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ 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.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket status after battery saver mode OFF event.
unique_ptr<LogEvent> batterySaverOffEvent =
@@ -4917,15 +6759,27 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
itBase->second.currentState.getValues()[0].mValue.int_value);
- // Value for key {{}, ON}
- ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+ // 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);
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, ON}
+ it++;
+ 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.intervals[0].hasValue);
EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Pull at end of first bucket.
vector<shared_ptr<LogEvent>> allData;
@@ -4944,6 +6798,15 @@
ASSERT_EQ(1, itBase->second.currentState.getValues().size());
EXPECT_EQ(BatterySaverModeStateChanged::OFF,
itBase->second.currentState.getValues()[0].mValue.int_value);
+ // Value for key {{}, OFF}
+ it = valueProducer->mCurrentSlicedBucket.begin();
+ assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+ // Value for key {{}, ON}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Bucket 2 status after condition change to false.
valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
@@ -4964,6 +6827,19 @@
it->first.getStateValuesKey().getValues()[0].mValue.int_value);
EXPECT_TRUE(it->second.intervals[0].hasValue);
EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+ assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+ bucket2StartTimeNs + 10 * NS_PER_SEC);
+ // Value for key {{}, ON}
+ it++;
+ 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_FALSE(it->second.intervals[0].hasValue);
+ assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+ // Value for key {{}, -1}
+ it++;
+ assertConditionTimer(it->second.conditionTimer, false, 0, 0);
// Start dump report and check output.
ProtoOutputStream output;
@@ -4982,6 +6858,7 @@
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());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
data = report.value_metrics().data(1);
EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
@@ -4990,6 +6867,8 @@
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());
+ EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+ EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
}
/*
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index 076000f..65e5875 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -920,14 +920,13 @@
// Create an initial config.
EXPECT_TRUE(initConfig(config));
- set<int64_t> replacedMatchers;
- set<int64_t> replacedConditions;
unordered_map<int64_t, int> metricToActivationMap;
- UpdateStatus status = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, status));
- EXPECT_EQ(status, UPDATE_PRESERVE);
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
}
TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) {
@@ -957,14 +956,13 @@
eventActivation->set_atom_matcher_id(startMatcher.id());
eventActivation->set_ttl_seconds(5);
- set<int64_t> replacedMatchers;
- set<int64_t> replacedConditions;
unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
- UpdateStatus status = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, status));
- EXPECT_EQ(status, UPDATE_REPLACE);
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+ metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) {
@@ -987,14 +985,13 @@
// Create an initial config.
EXPECT_TRUE(initConfig(config));
- set<int64_t> replacedMatchers = {whatMatcher.id()};
- set<int64_t> replacedConditions;
unordered_map<int64_t, int> metricToActivationMap;
- UpdateStatus status = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, status));
- EXPECT_EQ(status, UPDATE_REPLACE);
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) {
@@ -1017,14 +1014,13 @@
// Create an initial config.
EXPECT_TRUE(initConfig(config));
- set<int64_t> replacedMatchers;
- set<int64_t> replacedConditions = {predicate.id()};
unordered_map<int64_t, int> metricToActivationMap;
- UpdateStatus status = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, status));
- EXPECT_EQ(status, UPDATE_REPLACE);
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) {
@@ -1054,14 +1050,13 @@
// Create an initial config.
EXPECT_TRUE(initConfig(config));
- set<int64_t> replacedMatchers;
- set<int64_t> replacedConditions = {linkPredicate.id()};
unordered_map<int64_t, int> metricToActivationMap;
- UpdateStatus status = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, status));
- EXPECT_EQ(status, UPDATE_REPLACE);
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) {
@@ -1090,14 +1085,13 @@
// Create an initial config.
EXPECT_TRUE(initConfig(config));
- set<int64_t> replacedMatchers = {startMatcher.id()}; // The activation matcher is replaced.
- set<int64_t> replacedConditions;
unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
- UpdateStatus status = UPDATE_UNKNOWN;
- EXPECT_TRUE(determineEventMetricUpdateStatus(config, *metric, oldMetricProducerMap,
- oldMetricProducers, metricToActivationMap,
- replacedMatchers, replacedConditions, status));
- EXPECT_EQ(status, UPDATE_REPLACE);
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{},
+ /*replacedStates=*/{}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index a2d0b89..1f8cf8a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -46,39 +46,20 @@
// Access modes for handleIncomingUser.
- /**
- * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
- */
public static final int ALLOW_NON_FULL = 0;
/**
* Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
- * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in the same profile
- * group.
+ * if in the same profile group.
* Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
*/
- public static final int ALLOW_NON_FULL_IN_PROFILE_OR_FULL = 1;
- /**
- * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
- * only.
- */
+ public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
public static final int ALLOW_FULL_ONLY = 2;
/**
* Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
- * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in the same profile group.
+ * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group.
* Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
*/
- public static final int ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL = 3;
- /**
- * Requires {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS}, or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in same profile group,
- * otherwise {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
- * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. (so this is an extension
- * to {@link #ALLOW_NON_FULL})
- */
- public static final int ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL = 4;
+ public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
/**
* Verify that calling app has access to the given provider.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 312d122..bb194a5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3271,12 +3271,6 @@
sendMessage(H.CLEAN_UP_CONTEXT, cci);
}
- @Override
- public void handleFixedRotationAdjustments(@NonNull IBinder token,
- @Nullable FixedRotationAdjustments fixedRotationAdjustments) {
- 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
@@ -3286,51 +3280,39 @@
* @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<DisplayAdjustments> 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;
- }
+ @Override
+ public void handleFixedRotationAdjustments(@NonNull IBinder token,
+ @Nullable FixedRotationAdjustments fixedRotationAdjustments) {
+ final Consumer<DisplayAdjustments> override = fixedRotationAdjustments != null
+ ? displayAdjustments -> displayAdjustments
+ .setFixedRotationAdjustments(fixedRotationAdjustments)
+ : null;
if (!mResourcesManager.overrideTokenDisplayAdjustments(token, override)) {
// No resources are associated with the token.
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;
+ if (mActivities.get(token) == null) {
+ // Nothing to do for application if it is not an activity token.
+ return;
}
- // Apply the last override to application resources for compatibility. Because the Resources
- // of Display can be from application, e.g.
- // applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId)
- // and the deprecated usage:
- // applicationContext.getSystemService(WindowManager.class).getDefaultDisplay();
+ overrideApplicationDisplayAdjustments(token, override);
+ }
+
+ /**
+ * Applies the last override to application resources for compatibility. Because the Resources
+ * of Display can be from application, e.g.
+ * applicationContext.getSystemService(DisplayManager.class).getDisplay(displayId)
+ * and the deprecated usage:
+ * applicationContext.getSystemService(WindowManager.class).getDefaultDisplay();
+ *
+ * @param token The owner and target of the override.
+ * @param override The display adjustments override for application resources. If it is null,
+ * the override of the token will be removed and pop the last one to use.
+ */
+ private void overrideApplicationDisplayAdjustments(@NonNull IBinder token,
+ @Nullable Consumer<DisplayAdjustments> override) {
final Consumer<DisplayAdjustments> appOverride;
if (mActiveRotationAdjustments == null) {
mActiveRotationAdjustments = new ArrayList<>(2);
@@ -3559,8 +3541,13 @@
// 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,
- r.overrideConfig);
+ // The adjustments should have been set by handleLaunchActivity, so the last one is the
+ // override for activity resources.
+ if (mActiveRotationAdjustments != null && !mActiveRotationAdjustments.isEmpty()) {
+ mResourcesManager.overrideTokenDisplayAdjustments(r.token,
+ mActiveRotationAdjustments.get(
+ mActiveRotationAdjustments.size() - 1).second);
+ }
r.mPendingFixedRotationAdjustments = null;
}
@@ -3599,6 +3586,13 @@
mProfiler.startProfiling();
}
+ if (r.mPendingFixedRotationAdjustments != null) {
+ // The rotation adjustments must be applied before handling configuration, so process
+ // level display metrics can be adjusted.
+ overrideApplicationDisplayAdjustments(r.token, adjustments ->
+ adjustments.setFixedRotationAdjustments(r.mPendingFixedRotationAdjustments));
+ }
+
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
@@ -5735,7 +5729,15 @@
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
+ config);
- mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+ final Resources appResources = mInitialApplication.getResources();
+ if (appResources.hasOverrideDisplayAdjustments()) {
+ // The value of Display#getRealSize will be adjusted by FixedRotationAdjustments,
+ // but Display#getSize refers to DisplayAdjustments#mConfiguration. So the rotated
+ // configuration also needs to set to the adjustments for consistency.
+ appResources.getDisplayAdjustments().getConfiguration().updateFrom(config);
+ }
+ mResourcesManager.applyConfigurationToResourcesLocked(config, compat,
+ appResources.getDisplayAdjustments());
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
mResourcesManager.getConfiguration().getLocales());
@@ -7359,7 +7361,8 @@
// We need to apply this change to the resources immediately, because upon returning
// the view hierarchy will be informed about it.
if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
- null /* compat */)) {
+ null /* compat */,
+ mInitialApplication.getResources().getDisplayAdjustments())) {
updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
mResourcesManager.getConfiguration().getLocales());
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 04f72f6..167b5a8 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -6741,14 +6741,10 @@
*/
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(int code, int uid, @Mode int mode) {
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
try {
mService.setUidMode(code, uid, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
@@ -6766,7 +6762,11 @@
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
- setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
+ try {
+ mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** @hide */
@@ -6795,14 +6795,10 @@
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setMode(int code, int uid, String packageName, @Mode int mode) {
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
try {
mService.setMode(code, uid, packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
@@ -6822,7 +6818,11 @@
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setMode(@NonNull String op, int uid, @Nullable String packageName,
@Mode int mode) {
- setMode(strOpToOp(op), uid, packageName, mode);
+ try {
+ mService.setMode(strOpToOp(op), uid, packageName, mode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -7298,14 +7298,10 @@
* @hide
*/
public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) {
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
try {
return mService.checkOperationRaw(op, uid, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
@@ -7477,20 +7473,8 @@
}
}
- int mode;
- // Making the binder call "noteOperation" usually sets Binder.callingUid to the calling
- // processes UID. Hence clearing the calling UID is superfluous.
- // If the call is inside the system server though "noteOperation" is not a binder all,
- // it is only a method call. Hence Binder.callingUid might still be set to the app that
- // called the system server. This can lead to problems as not every app can see the
- // same appops the system server can see.
- long token = Binder.clearCallingIdentity();
- try {
- mode = mService.noteOperation(op, uid, packageName, attributionTag,
- collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ int mode = mService.noteOperation(op, uid, packageName, attributionTag,
+ collectionMode == COLLECT_ASYNC, message, shouldCollectMessage);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
@@ -7653,17 +7637,10 @@
}
}
- int mode;
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
- try {
- mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
- proxiedAttributionTag, myUid, mContext.getOpPackageName(),
- mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message,
- shouldCollectMessage);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
+ proxiedAttributionTag, myUid, mContext.getOpPackageName(),
+ mContext.getAttributionTag(), collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
@@ -7713,8 +7690,6 @@
*/
@UnsupportedAppUsage
public int checkOp(int op, int uid, String packageName) {
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
try {
int mode = mService.checkOperation(op, uid, packageName);
if (mode == MODE_ERRORED) {
@@ -7723,8 +7698,6 @@
return mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
@@ -7735,15 +7708,11 @@
*/
@UnsupportedAppUsage
public int checkOpNoThrow(int op, int uid, String packageName) {
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
try {
int mode = mService.checkOperation(op, uid, packageName);
return mode == AppOpsManager.MODE_FOREGROUND ? AppOpsManager.MODE_ALLOWED : mode;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
@@ -7995,16 +7964,9 @@
}
}
- int mode;
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
- try {
- mode = mService.startOperation(getClientId(), op, uid, packageName,
- attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC,
- message, shouldCollectMessage);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ int mode = mService.startOperation(getClientId(), op, uid, packageName,
+ attributionTag, startIfModeDefault, collectionMode == COLLECT_ASYNC, message,
+ shouldCollectMessage);
if (mode == MODE_ALLOWED) {
if (collectionMode == COLLECT_SELF) {
@@ -8067,14 +8029,10 @@
*/
public void finishOp(int op, int uid, @NonNull String packageName,
@Nullable String attributionTag) {
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
try {
mService.finishOperation(getClientId(), op, uid, packageName, attributionTag);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
@@ -8666,14 +8624,10 @@
// TODO: Uncomment below annotation once b/73559440 is fixed
// @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
public boolean isOperationActive(int code, int uid, String packageName) {
- // Clear calling UID to handle calls from inside the system server. See #noteOpNoThrow
- long token = Binder.clearCallingIdentity();
try {
return mService.isOperationActive(code, uid, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index cf5a7b6..e94fd45 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -187,16 +187,6 @@
private static final String XATTR_INODE_CODE_CACHE = "user.inode_code_cache";
/**
- * Special intent extra that critical system apps can use to hide the notification for a
- * foreground service. This extra should be placed in the intent passed into {@link
- * #startForegroundService(Intent)}.
- *
- * @hide
- */
- private static final String EXTRA_HIDDEN_FOREGROUND_SERVICE =
- "android.intent.extra.HIDDEN_FOREGROUND_SERVICE";
-
- /**
* Map from package name, to preference name, to cached preferences.
*/
@GuardedBy("ContextImpl.class")
@@ -1717,12 +1707,9 @@
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
- final boolean hideForegroundNotification = requireForeground
- && service.getBooleanExtra(EXTRA_HIDDEN_FOREGROUND_SERVICE, false);
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
- hideForegroundNotification,
getOpPackageName(), getAttributionTag(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index c1e6f52..95bbebe 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -156,8 +156,7 @@
boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta);
PendingIntent getRunningServiceControlPanel(in ComponentName service);
ComponentName startService(in IApplicationThread caller, in Intent service,
- in String resolvedType, boolean requireForeground,
- boolean hideForegroundNotification, in String callingPackage,
+ in String resolvedType, boolean requireForeground, in String callingPackage,
in String callingFeatureId, int userId);
@UnsupportedAppUsage
int stopService(in IApplicationThread caller, in Intent service,
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 4c9e400..9eeb9f6 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -52,4 +52,6 @@
void dropShellPermissionIdentity();
// Called from the system process.
oneway void shutdown();
+ void executeShellCommandWithStderr(String command, in ParcelFileDescriptor sink,
+ in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink);
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 9e4ab33..b267840 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -1262,12 +1262,18 @@
public final boolean applyConfigurationToResources(@NonNull Configuration config,
@Nullable CompatibilityInfo compat) {
synchronized(this) {
- return applyConfigurationToResourcesLocked(config, compat);
+ return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
}
}
public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
- @Nullable CompatibilityInfo compat) {
+ @Nullable CompatibilityInfo compat) {
+ return applyConfigurationToResourcesLocked(config, compat, null /* adjustments */);
+ }
+
+ /** Applies the global configuration to the managed resources. */
+ public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config,
+ @Nullable CompatibilityInfo compat, @Nullable DisplayAdjustments adjustments) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyConfigurationToResourcesLocked");
@@ -1291,6 +1297,11 @@
}
DisplayMetrics displayMetrics = getDisplayMetrics();
+ if (adjustments != null) {
+ // Currently the only case where the adjustment takes effect is to simulate placing
+ // an app in a rotated display.
+ adjustments.adjustGlobalAppMetrics(displayMetrics);
+ }
Resources.updateSystemConfiguration(config, displayMetrics, compat);
ApplicationPackageManager.configurationChanged();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 4ff9e8d..99d2127 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -402,7 +402,7 @@
@TestApi
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
- public void setDisabledForSimNetworkLock(boolean disabled) {
+ public void setExpansionDisabledForSimNetworkLock(boolean disabled) {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
final IStatusBarService svc = getService();
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index ab997e9..4e868fe 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -25,6 +25,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Bitmap;
@@ -1245,7 +1246,34 @@
* @hide
*/
@TestApi
- public ParcelFileDescriptor[] executeShellCommandRw(String command) {
+ public @NonNull ParcelFileDescriptor[] executeShellCommandRw(@NonNull String command) {
+ return executeShellCommandInternal(command, false /* includeStderr */);
+ }
+
+ /**
+ * Executes a shell command. This method returns three file descriptors,
+ * one that points to the standard output stream (element at index 0), one that points
+ * to the standard input stream (element at index 1), and one points to
+ * standard error stream (element at index 2). The command execution is similar
+ * to running "adb shell <command>" from a host connected to the device.
+ * <p>
+ * <strong>Note:</strong> It is your responsibility to close the returned file
+ * descriptors once you are done reading/writing.
+ * </p>
+ *
+ * @param command The command to execute.
+ * @return File descriptors (out, in, err) to the standard output/input/error streams.
+ *
+ * @hide
+ */
+ @TestApi
+ @SuppressLint("ArrayReturn") // For consistency with other APIs
+ public @NonNull ParcelFileDescriptor[] executeShellCommandRwe(@NonNull String command) {
+ return executeShellCommandInternal(command, true /* includeStderr */);
+ }
+
+ private ParcelFileDescriptor[] executeShellCommandInternal(
+ String command, boolean includeStderr) {
warnIfBetterCommand(command);
ParcelFileDescriptor source_read = null;
@@ -1254,6 +1282,9 @@
ParcelFileDescriptor source_write = null;
ParcelFileDescriptor sink_write = null;
+ ParcelFileDescriptor stderr_source_read = null;
+ ParcelFileDescriptor stderr_sink_read = null;
+
try {
ParcelFileDescriptor[] pipe_read = ParcelFileDescriptor.createPipe();
source_read = pipe_read[0];
@@ -1263,8 +1294,15 @@
source_write = pipe_write[0];
sink_write = pipe_write[1];
+ if (includeStderr) {
+ ParcelFileDescriptor[] stderr_read = ParcelFileDescriptor.createPipe();
+ stderr_source_read = stderr_read[0];
+ stderr_sink_read = stderr_read[1];
+ }
+
// Calling out without a lock held.
- mUiAutomationConnection.executeShellCommand(command, sink_read, source_write);
+ mUiAutomationConnection.executeShellCommandWithStderr(
+ command, sink_read, source_write, stderr_sink_read);
} catch (IOException ioe) {
Log.e(LOG_TAG, "Error executing shell command!", ioe);
} catch (RemoteException re) {
@@ -1272,11 +1310,15 @@
} finally {
IoUtils.closeQuietly(sink_read);
IoUtils.closeQuietly(source_write);
+ IoUtils.closeQuietly(stderr_sink_read);
}
- ParcelFileDescriptor[] result = new ParcelFileDescriptor[2];
+ ParcelFileDescriptor[] result = new ParcelFileDescriptor[includeStderr ? 3 : 2];
result[0] = source_read;
result[1] = sink_write;
+ if (includeStderr) {
+ result[2] = stderr_source_read;
+ }
return result;
}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 70d5201..255b93f 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -372,6 +372,13 @@
@Override
public void executeShellCommand(final String command, final ParcelFileDescriptor sink,
final ParcelFileDescriptor source) throws RemoteException {
+ executeShellCommandWithStderr(command, sink, source, null /* stderrSink */);
+ }
+
+ @Override
+ public void executeShellCommandWithStderr(final String command, final ParcelFileDescriptor sink,
+ final ParcelFileDescriptor source, final ParcelFileDescriptor stderrSink)
+ throws RemoteException {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
@@ -409,6 +416,18 @@
writeToProcess = null;
}
+ // Read from process stderr and write to pipe
+ final Thread readStderrFromProcess;
+ if (stderrSink != null) {
+ InputStream sink_in = process.getErrorStream();
+ OutputStream sink_out = new FileOutputStream(stderrSink.getFileDescriptor());
+
+ readStderrFromProcess = new Thread(new Repeater(sink_in, sink_out));
+ readStderrFromProcess.start();
+ } else {
+ readStderrFromProcess = null;
+ }
+
Thread cleanup = new Thread(new Runnable() {
@Override
public void run() {
@@ -419,14 +438,18 @@
if (readFromProcess != null) {
readFromProcess.join();
}
+ if (readStderrFromProcess != null) {
+ readStderrFromProcess.join();
+ }
} catch (InterruptedException exc) {
Log.e(TAG, "At least one of the threads was interrupted");
}
IoUtils.closeQuietly(sink);
IoUtils.closeQuietly(source);
+ IoUtils.closeQuietly(stderrSink);
process.destroy();
- }
- });
+ }
+ });
cleanup.start();
}
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index e07bc02..a52fc89 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -51,19 +51,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface SourceCodecType {}
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_SBC = 0;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_AAC = 1;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_APTX = 2;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_LDAC = 4;
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_MAX = 5;
-
+ @UnsupportedAppUsage
public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
/** @hide */
@@ -75,10 +81,13 @@
@Retention(RetentionPolicy.SOURCE)
public @interface CodecPriority {}
+ @UnsupportedAppUsage
public static final int CODEC_PRIORITY_DISABLED = -1;
+ @UnsupportedAppUsage
public static final int CODEC_PRIORITY_DEFAULT = 0;
+ @UnsupportedAppUsage
public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
@@ -95,18 +104,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface SampleRate {}
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_NONE = 0;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_44100 = 0x1 << 0;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_48000 = 0x1 << 1;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_88200 = 0x1 << 2;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_96000 = 0x1 << 3;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_176400 = 0x1 << 4;
+ @UnsupportedAppUsage
public static final int SAMPLE_RATE_192000 = 0x1 << 5;
@@ -120,12 +136,16 @@
@Retention(RetentionPolicy.SOURCE)
public @interface BitsPerSample {}
+ @UnsupportedAppUsage
public static final int BITS_PER_SAMPLE_NONE = 0;
+ @UnsupportedAppUsage
public static final int BITS_PER_SAMPLE_16 = 0x1 << 0;
+ @UnsupportedAppUsage
public static final int BITS_PER_SAMPLE_24 = 0x1 << 1;
+ @UnsupportedAppUsage
public static final int BITS_PER_SAMPLE_32 = 0x1 << 2;
@@ -138,10 +158,13 @@
@Retention(RetentionPolicy.SOURCE)
public @interface ChannelMode {}
+ @UnsupportedAppUsage
public static final int CHANNEL_MODE_NONE = 0;
+ @UnsupportedAppUsage
public static final int CHANNEL_MODE_MONO = 0x1 << 0;
+ @UnsupportedAppUsage
public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
private final @SourceCodecType int mCodecType;
@@ -154,6 +177,7 @@
private final long mCodecSpecific3;
private final long mCodecSpecific4;
+ @UnsupportedAppUsage
public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority,
@SampleRate int sampleRate, @BitsPerSample int bitsPerSample,
@ChannelMode int channelMode, long codecSpecific1,
@@ -170,6 +194,7 @@
mCodecSpecific4 = codecSpecific4;
}
+ @UnsupportedAppUsage
public BluetoothCodecConfig(@SourceCodecType int codecType) {
mCodecType = codecType;
mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
@@ -391,6 +416,7 @@
*
* @return the codec type
*/
+ @UnsupportedAppUsage
public @SourceCodecType int getCodecType() {
return mCodecType;
}
@@ -411,6 +437,7 @@
*
* @return the codec priority
*/
+ @UnsupportedAppUsage
public @CodecPriority int getCodecPriority() {
return mCodecPriority;
}
@@ -441,6 +468,7 @@
*
* @return the codec sample rate
*/
+ @UnsupportedAppUsage
public @SampleRate int getSampleRate() {
return mSampleRate;
}
@@ -455,6 +483,7 @@
*
* @return the codec bits per sample
*/
+ @UnsupportedAppUsage
public @BitsPerSample int getBitsPerSample() {
return mBitsPerSample;
}
@@ -479,6 +508,7 @@
*
* @return a codec specific value1.
*/
+ @UnsupportedAppUsage
public long getCodecSpecific1() {
return mCodecSpecific1;
}
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
index 1e394b8..7b567b4 100644
--- a/core/java/android/bluetooth/BluetoothCodecStatus.java
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -17,6 +17,7 @@
package android.bluetooth;
import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -38,6 +39,7 @@
* This extra represents the current codec status of the A2DP
* profile.
*/
+ @UnsupportedAppUsage
public static final String EXTRA_CODEC_STATUS =
"android.bluetooth.extra.CODEC_STATUS";
@@ -196,6 +198,7 @@
*
* @return the current codec configuration
*/
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig getCodecConfig() {
return mCodecConfig;
}
@@ -205,6 +208,7 @@
*
* @return an array with the codecs local capabilities
*/
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() {
return mCodecsLocalCapabilities;
}
@@ -214,6 +218,7 @@
*
* @return an array with the codecs selectable capabilities
*/
+ @UnsupportedAppUsage
public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() {
return mCodecsSelectableCapabilities;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5452f92..005648f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -351,6 +351,13 @@
* due to its foreground state such as an activity or foreground service, then this flag will
* allow the bound app to get the same capabilities, as long as it has the required permissions
* as well.
+ *
+ * If binding from a top app and its target SDK version is at or above
+ * {@link android.os.Build.VERSION_CODES#R}, the app needs to
+ * explicitly use BIND_INCLUDE_CAPABILITIES flag to pass all capabilities to the service so the
+ * other app can have while-use-use access such as location, camera, microphone from background.
+ * If binding from a top app and its target SDK version is below
+ * {@link android.os.Build.VERSION_CODES#R}, BIND_INCLUDE_CAPABILITIES is implicit.
*/
public static final int BIND_INCLUDE_CAPABILITIES = 0x000001000;
@@ -3179,8 +3186,8 @@
* {@link #BIND_AUTO_CREATE}, {@link #BIND_DEBUG_UNBIND},
* {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
* {@link #BIND_ALLOW_OOM_MANAGEMENT}, {@link #BIND_WAIVE_PRIORITY}.
- * {@link #BIND_IMPORTANT}, or
- * {@link #BIND_ADJUST_WITH_ACTIVITY}.
+ * {@link #BIND_IMPORTANT}, {@link #BIND_ADJUST_WITH_ACTIVITY},
+ * {@link #BIND_NOT_PERCEPTIBLE}, or {@link #BIND_INCLUDE_CAPABILITIES}.
* @return {@code true} if the system is in the process of bringing up a
* service that your client has permission to bind to; {@code false}
* if the system couldn't find the service or if your client doesn't
@@ -3201,6 +3208,8 @@
* @see #BIND_WAIVE_PRIORITY
* @see #BIND_IMPORTANT
* @see #BIND_ADJUST_WITH_ACTIVITY
+ * @see #BIND_NOT_PERCEPTIBLE
+ * @see #BIND_INCLUDE_CAPABILITIES
*/
public abstract boolean bindService(@RequiresPermission Intent service,
@NonNull ServiceConnection conn, @BindServiceFlags int flags);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index af2b905..24282365 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -793,4 +793,6 @@
List<String> getMimeGroup(String packageName, String group);
boolean isAutoRevokeWhitelisted(String packageName);
+
+ void grantImplicitAccess(int queryingUid, String visibleAuthority);
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index da56abf..a9f1439 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1878,6 +1878,7 @@
/** {@hide} */
@SystemApi
+ @TestApi
public void setInstallAsInstantApp(boolean isInstantApp) {
if (isInstantApp) {
installFlags |= PackageManager.INSTALL_INSTANT_APP;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f0b2329..4eec56c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8085,6 +8085,20 @@
"getMimeGroup not implemented in subclass");
}
+ /**
+ * Grants implicit visibility of the package that provides an authority to a querying UID.
+ *
+ * @throws SecurityException when called by a package other than the contacts provider
+ * @hide
+ */
+ public void grantImplicitAccess(int queryingUid, String visibleAuthority) {
+ try {
+ ActivityThread.getPackageManager().grantImplicitAccess(queryingUid, visibleAuthority);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
// Some of the flags don't affect the query result, but let's be conservative and cache
// each combination of flags separately.
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index f86eb90..8d0cc68 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -103,6 +103,7 @@
@IntDef(flag = true, value = {
BIOMETRIC_STRONG,
BIOMETRIC_WEAK,
+ BIOMETRIC_CONVENIENCE,
DEVICE_CREDENTIAL,
})
@interface Types {}
diff --git a/core/java/android/hardware/biometrics/SensorProperties.java b/core/java/android/hardware/biometrics/SensorProperties.java
new file mode 100644
index 0000000..0f33ac4
--- /dev/null
+++ b/core/java/android/hardware/biometrics/SensorProperties.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The base class containing all sensor-agnostic information. This is a superset of the
+ * {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible
+ * behavior with the older generation of HIDL (non-AIDL) interfaces.
+ * @hide
+ */
+public class SensorProperties implements Parcelable {
+
+ public static final int STRENGTH_CONVENIENCE = 0;
+ public static final int STRENGTH_WEAK = 1;
+ public static final int STRENGTH_STRONG = 2;
+
+ @IntDef({STRENGTH_CONVENIENCE, STRENGTH_WEAK, STRENGTH_STRONG})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Strength {}
+
+ public final int sensorId;
+ @Strength public final int sensorStrength;
+ public final int maxEnrollmentsPerUser;
+
+ protected SensorProperties(int sensorId, @Strength int sensorStrength,
+ int maxEnrollmentsPerUser) {
+ this.sensorId = sensorId;
+ this.sensorStrength = sensorStrength;
+ this.maxEnrollmentsPerUser = maxEnrollmentsPerUser;
+ }
+
+ protected SensorProperties(Parcel in) {
+ sensorId = in.readInt();
+ sensorStrength = in.readInt();
+ maxEnrollmentsPerUser = in.readInt();
+ }
+
+ public static final Creator<SensorProperties> CREATOR = new Creator<SensorProperties>() {
+ @Override
+ public SensorProperties createFromParcel(Parcel in) {
+ return new SensorProperties(in);
+ }
+
+ @Override
+ public SensorProperties[] newArray(int size) {
+ return new SensorProperties[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(sensorId);
+ dest.writeInt(sensorStrength);
+ dest.writeInt(maxEnrollmentsPerUser);
+ }
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8f99edf..dda1890b 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -941,6 +941,7 @@
*/
String KEY_PEAK_REFRESH_RATE_DEFAULT = "peak_refresh_rate_default";
+ // TODO(b/162536543): rename it once it is proved not harmful for users.
/**
* Key for controlling which packages are explicitly blocked from running at refresh rates
* higher than 60hz. An app may be added to this list if they exhibit performance issues at
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 437feb1..e744c84 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -110,5 +110,5 @@
String opPackageName);
// Give FaceService its ID. See AuthService.java
- void initializeConfiguration(int sensorId);
+ void initializeConfiguration(int sensorId, int strength);
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
index 2fd0068..718141a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
@@ -17,6 +17,7 @@
package android.hardware.fingerprint;
import android.annotation.IntDef;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceSensorProperties;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,45 +29,44 @@
* Container for fingerprint sensor properties.
* @hide
*/
-public class FingerprintSensorProperties implements Parcelable {
+public class FingerprintSensorProperties extends SensorProperties {
public static final int TYPE_UNKNOWN = 0;
public static final int TYPE_REAR = 1;
- public static final int TYPE_UDFPS = 2;
- public static final int TYPE_POWER_BUTTON = 3;
+ public static final int TYPE_UDFPS_ULTRASONIC = 2;
+ public static final int TYPE_UDFPS_OPTICAL = 3;
+ public static final int TYPE_POWER_BUTTON = 4;
+ public static final int TYPE_HOME_BUTTON = 5;
- @IntDef({
- TYPE_UNKNOWN,
+ @IntDef({TYPE_UNKNOWN,
TYPE_REAR,
- TYPE_UDFPS,
- TYPE_POWER_BUTTON})
+ TYPE_UDFPS_ULTRASONIC,
+ TYPE_UDFPS_OPTICAL,
+ TYPE_POWER_BUTTON,
+ TYPE_HOME_BUTTON})
@Retention(RetentionPolicy.SOURCE)
public @interface SensorType {}
- public final int sensorId;
public final @SensorType int sensorType;
// IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
// cannot be checked
public final boolean resetLockoutRequiresHardwareAuthToken;
- // Maximum number of enrollments a user/profile can have.
- public final int maxTemplatesAllowed;
/**
* Initializes SensorProperties with specified values
*/
- public FingerprintSensorProperties(int sensorId, @SensorType int sensorType,
- boolean resetLockoutRequiresHardwareAuthToken, int maxTemplatesAllowed) {
- this.sensorId = sensorId;
+ public FingerprintSensorProperties(int sensorId, @Strength int strength,
+ int maxEnrollmentsPerUser, @SensorType int sensorType,
+ boolean resetLockoutRequiresHardwareAuthToken) {
+ super(sensorId, strength, maxEnrollmentsPerUser);
this.sensorType = sensorType;
this.resetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
- this.maxTemplatesAllowed = maxTemplatesAllowed;
}
protected FingerprintSensorProperties(Parcel in) {
- sensorId = in.readInt();
+ super(in);
sensorType = in.readInt();
resetLockoutRequiresHardwareAuthToken = in.readBoolean();
- maxTemplatesAllowed = in.readInt();
}
public static final Creator<FingerprintSensorProperties> CREATOR =
@@ -89,9 +89,18 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(sensorId);
+ super.writeToParcel(dest, flags);
dest.writeInt(sensorType);
dest.writeBoolean(resetLockoutRequiresHardwareAuthToken);
- dest.writeInt(maxTemplatesAllowed);
+ }
+
+ public boolean isAnyUdfpsType() {
+ switch (sensorType) {
+ case TYPE_UDFPS_OPTICAL:
+ case TYPE_UDFPS_ULTRASONIC:
+ return true;
+ default:
+ return false;
+ }
}
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 0fae156..cc2b520 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -118,7 +118,7 @@
void removeClientActiveCallback(IFingerprintClientActiveCallback callback);
// Give FingerprintService its ID. See AuthService.java
- void initializeConfiguration(int sensorId);
+ void initializeConfiguration(int sensorId, int strength);
// Notifies about a finger touching the sensor area.
void onFingerDown(int x, int y, float minor, float major);
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index c4d123c..06b5b67 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -32,10 +32,12 @@
import android.media.soundtrigger_middleware.SoundModel;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
+import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
import android.system.ErrnoException;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.UUID;
@@ -109,7 +111,12 @@
aidlModel.type = apiModel.getType();
aidlModel.uuid = api2aidlUuid(apiModel.getUuid());
aidlModel.vendorUuid = api2aidlUuid(apiModel.getVendorUuid());
- aidlModel.data = byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel");
+ try {
+ aidlModel.data = ParcelFileDescriptor.dup(
+ byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel"));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
aidlModel.dataSize = apiModel.getData().length;
return aidlModel;
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 60ddd8a..d62d1e1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -16,10 +16,10 @@
package android.inputmethodservice;
+import static android.graphics.Color.TRANSPARENT;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowInsets.Type.navigationBars;
-import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -69,7 +69,6 @@
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.Window;
-import android.view.WindowInsets;
import android.view.WindowInsets.Side;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
@@ -1204,25 +1203,22 @@
Context.LAYOUT_INFLATER_SERVICE);
mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState,
WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
- mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
+ mWindow.getWindow().getAttributes().setFitInsetsTypes(navigationBars());
mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
- // IME layout should always be inset by navigation bar, no matter its current visibility,
- // unless automotive requests it. Automotive devices may request the navigation bar to be
- // hidden when the IME shows up (controlled via config_automotiveHideNavBarForKeyboard)
- // in order to maximize the visible screen real estate. When this happens, the IME window
- // should animate from the bottom of the screen to reduce the jank that happens from the
- // lack of synchronization between the bottom system window and the IME window.
+ // Our window will extend into the status bar area no matter the bar is visible or not.
+ // We don't want the ColorView to be visible when status bar is shown.
+ mWindow.getWindow().setStatusBarColor(TRANSPARENT);
+
+ // Automotive devices may request the navigation bar to be hidden when the IME shows up
+ // (controlled via config_automotiveHideNavBarForKeyboard) in order to maximize the visible
+ // screen real estate. When this happens, the IME window should animate from the bottom of
+ // the screen to reduce the jank that happens from the lack of synchronization between the
+ // bottom system window and the IME window.
if (mIsAutomotive && mAutomotiveHideNavBarForKeyboard) {
mWindow.getWindow().setDecorFitsSystemWindows(false);
}
- mWindow.getWindow().getDecorView().setOnApplyWindowInsetsListener(
- (v, insets) -> v.onApplyWindowInsets(
- new WindowInsets.Builder(insets).setInsets(
- navigationBars(),
- insets.getInsetsIgnoringVisibility(navigationBars()))
- .build()));
// For ColorView in DecorView to work, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS needs to be set
// by default (but IME developers can opt this out later if they want a new behavior).
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1db544c..78ba7f0 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -141,8 +141,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 4b2cfe2..a2e53e2 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2551,14 +2551,16 @@
public static native long getZramFreeKb();
/**
- * Return memory size in kilobytes allocated for ION heaps.
+ * Return memory size in kilobytes allocated for ION heaps or -1 if
+ * /sys/kernel/ion/total_heaps_kb could not be read.
*
* @hide
*/
public static native long getIonHeapsSizeKb();
/**
- * Return memory size in kilobytes allocated for ION pools.
+ * Return memory size in kilobytes allocated for ION pools or -1 if
+ * /sys/kernel/ion/total_pools_kb could not be read.
*
* @hide
*/
diff --git a/core/java/android/os/IVibratorManagerService.aidl b/core/java/android/os/IVibratorManagerService.aidl
new file mode 100644
index 0000000..e821e31
--- /dev/null
+++ b/core/java/android/os/IVibratorManagerService.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.VibrationAttributes;
+
+/** {@hide} */
+interface IVibratorManagerService {
+ int[] getVibratorIds();
+}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 40c291f..91eb2a5 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -3,6 +3,7 @@
per-file ExternalVibration.java = michaelwr@google.com
per-file IExternalVibrationController.aidl = michaelwr@google.com
per-file IExternalVibratorService.aidl = michaelwr@google.com
+per-file IVibratorManagerService.aidl = michaelwr@google.com
per-file IVibratorService.aidl = michaelwr@google.com
per-file NullVibrator.java = michaelwr@google.com
per-file SystemVibrator.java = michaelwr@google.com
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index b15109e..d80a7e7 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -34,7 +34,6 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.permission.SplitPermissionInfoParcelable;
-import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -544,15 +543,10 @@
+ permission);
return PackageManager.PERMISSION_DENIED;
}
- // Clear Binder.callingUid in case this is called inside the system server. See
- // more extensive comment in checkPackageNamePermissionUncached
- long token = Binder.clearCallingIdentity();
try {
return am.checkPermission(permission, pid, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
@@ -685,20 +679,11 @@
/* @hide */
private static int checkPackageNamePermissionUncached(
String permName, String pkgName, @UserIdInt int userId) {
- // Makeing the binder call "checkPermission" usually sets Binder.callingUid to the calling
- // processes UID. Hence clearing the calling UID is superflous.
- // If the call is inside the system server though "checkPermission" is not a binder all, it
- // is only a method call. Hence Binder.callingUid might still be set to the app that called
- // the system server. This can lead to problems as not every app can check the same
- // permissions the system server can check.
- long token = Binder.clearCallingIdentity();
try {
return ActivityThread.getPermissionManager().checkPermission(
permName, pkgName, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } finally {
- Binder.restoreCallingIdentity(token);
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8a5963e..07867e2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7954,6 +7954,12 @@
public static final String UI_NIGHT_MODE_OVERRIDE_ON = "ui_night_mode_override_on";
/**
+ * The last computed night mode bool the last time the phone was on
+ * @hide
+ */
+ public static final String UI_NIGHT_MODE_LAST_COMPUTED = "ui_night_mode_last_computed";
+
+ /**
* The current night mode that has been overridden to turn off by the system. Owned
* and controlled by UiModeManagerService. Constants are as per
* UiModeManager.
@@ -9078,6 +9084,22 @@
};
/**
+ * How long Assistant handles have enabled in milliseconds.
+ *
+ * @hide
+ */
+ public static final String ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS =
+ "reminder_exp_learning_time_elapsed";
+
+ /**
+ * How many times the Assistant has been triggered using the touch gesture.
+ *
+ * @hide
+ */
+ public static final String ASSIST_HANDLES_LEARNING_EVENT_COUNT =
+ "reminder_exp_learning_event_count";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
@@ -9686,6 +9708,14 @@
public static final String DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR =
"render_shadows_in_compositor";
+ /**
+ * If true, submit buffers using blast in SurfaceView.
+ * (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String DEVELOPMENT_USE_BLAST_ADAPTER_SV =
+ "use_blast_adapter_sv";
+
/**
* Whether user has enabled development settings.
*/
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index 5a74ec0..b77265b 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -144,6 +144,17 @@
}
/**
+ * Adds the values in the specified array to this array.
+ */
+ public void addAll(int[] values) {
+ final int count = values.length;
+ ensureCapacity(count);
+
+ System.arraycopy(values, 0, mValues, mSize, count);
+ mSize += count;
+ }
+
+ /**
* Ensures capacity to append at least <code>count</code> values.
*/
private void ensureCapacity(int count) {
diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java
index c726bee..7c01f7a8 100644
--- a/core/java/android/view/DisplayAdjustments.java
+++ b/core/java/android/view/DisplayAdjustments.java
@@ -130,14 +130,16 @@
w = metrics.noncompatWidthPixels;
metrics.noncompatWidthPixels = metrics.noncompatHeightPixels;
metrics.noncompatHeightPixels = w;
+ }
- float x = metrics.xdpi;
- metrics.xdpi = metrics.ydpi;
- metrics.ydpi = x;
-
- x = metrics.noncompatXdpi;
- metrics.noncompatXdpi = metrics.noncompatYdpi;
- metrics.noncompatYdpi = x;
+ /** Adjusts global display metrics that is available to applications. */
+ public void adjustGlobalAppMetrics(@NonNull DisplayMetrics metrics) {
+ final FixedRotationAdjustments rotationAdjustments = mFixedRotationAdjustments;
+ if (rotationAdjustments == null) {
+ return;
+ }
+ metrics.noncompatWidthPixels = metrics.widthPixels = rotationAdjustments.mAppWidth;
+ metrics.noncompatHeightPixels = metrics.heightPixels = rotationAdjustments.mAppHeight;
}
/** Returns the adjusted cutout if available. Otherwise the original cutout is returned. */
@@ -178,7 +180,7 @@
/**
* An application can be launched in different rotation than the real display. This class
- * provides the information to adjust the values returned by {@link #Display}.
+ * provides the information to adjust the values returned by {@link Display}.
* @hide
*/
public static class FixedRotationAdjustments implements Parcelable {
@@ -186,12 +188,24 @@
@Surface.Rotation
final int mRotation;
+ /**
+ * The rotated {@link DisplayInfo#appWidth}. The value cannot be simply swapped according
+ * to rotation because it minus the region of screen decorations.
+ */
+ final int mAppWidth;
+
+ /** The rotated {@link DisplayInfo#appHeight}. */
+ final int mAppHeight;
+
/** Non-null if the device has cutout. */
@Nullable
final DisplayCutout mRotatedDisplayCutout;
- public FixedRotationAdjustments(@Surface.Rotation int rotation, DisplayCutout cutout) {
+ public FixedRotationAdjustments(@Surface.Rotation int rotation, int appWidth, int appHeight,
+ DisplayCutout cutout) {
mRotation = rotation;
+ mAppWidth = appWidth;
+ mAppHeight = appHeight;
mRotatedDisplayCutout = cutout;
}
@@ -199,6 +213,8 @@
public int hashCode() {
int hash = 17;
hash = hash * 31 + mRotation;
+ hash = hash * 31 + mAppWidth;
+ hash = hash * 31 + mAppHeight;
hash = hash * 31 + Objects.hashCode(mRotatedDisplayCutout);
return hash;
}
@@ -210,12 +226,14 @@
}
final FixedRotationAdjustments other = (FixedRotationAdjustments) o;
return mRotation == other.mRotation
+ && mAppWidth == other.mAppWidth && mAppHeight == other.mAppHeight
&& Objects.equals(mRotatedDisplayCutout, other.mRotatedDisplayCutout);
}
@Override
public String toString() {
return "FixedRotationAdjustments{rotation=" + Surface.rotationToString(mRotation)
+ + " appWidth=" + mAppWidth + " appHeight=" + mAppHeight
+ " cutout=" + mRotatedDisplayCutout + "}";
}
@@ -227,12 +245,16 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mRotation);
+ dest.writeInt(mAppWidth);
+ dest.writeInt(mAppHeight);
dest.writeTypedObject(
new DisplayCutout.ParcelableWrapper(mRotatedDisplayCutout), flags);
}
private FixedRotationAdjustments(Parcel in) {
mRotation = in.readInt();
+ mAppWidth = in.readInt();
+ mAppHeight = in.readInt();
final DisplayCutout.ParcelableWrapper cutoutWrapper =
in.readTypedObject(DisplayCutout.ParcelableWrapper.CREATOR);
mRotatedDisplayCutout = cutoutWrapper != null ? cutoutWrapper.get() : null;
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index e1b0421..f2d3f5a 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -83,7 +83,7 @@
*
* @hide
*/
- public void setNativeInputChannel(long nativeChannel) {
+ private void setNativeInputChannel(long nativeChannel) {
if (nativeChannel == 0) {
throw new IllegalArgumentException("Attempting to set native input channel to null.");
}
@@ -148,12 +148,11 @@
}
/**
- * Transfers ownership of the internal state of the input channel to another
- * instance and invalidates this instance. This is used to pass an input channel
+ * Creates a copy of this instance to the outParameter. This is used to pass an input channel
* as an out parameter in a binder call.
* @param other The other input channel instance.
*/
- public void transferTo(InputChannel outParameter) {
+ public void copyTo(InputChannel outParameter) {
if (outParameter == null) {
throw new IllegalArgumentException("outParameter must not be null");
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index f462a99..c5d0a10 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -76,6 +76,10 @@
ITYPE_BOTTOM_GESTURES,
ITYPE_LEFT_GESTURES,
ITYPE_RIGHT_GESTURES,
+ ITYPE_TOP_MANDATORY_GESTURES,
+ ITYPE_BOTTOM_MANDATORY_GESTURES,
+ ITYPE_LEFT_MANDATORY_GESTURES,
+ ITYPE_RIGHT_MANDATORY_GESTURES,
ITYPE_TOP_TAPPABLE_ELEMENT,
ITYPE_BOTTOM_TAPPABLE_ELEMENT,
ITYPE_LEFT_DISPLAY_CUTOUT,
@@ -104,20 +108,27 @@
public static final int ITYPE_BOTTOM_GESTURES = 4;
public static final int ITYPE_LEFT_GESTURES = 5;
public static final int ITYPE_RIGHT_GESTURES = 6;
- public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 7;
- public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 8;
- public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 9;
- public static final int ITYPE_TOP_DISPLAY_CUTOUT = 10;
- public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 11;
- public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 12;
+ /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */
+ public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
+ public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
+ public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
+ public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
+
+ public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 11;
+ public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 12;
+
+ public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 13;
+ public static final int ITYPE_TOP_DISPLAY_CUTOUT = 14;
+ public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 15;
+ public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 16;
/** Input method window. */
- public static final int ITYPE_IME = 13;
+ public static final int ITYPE_IME = 17;
/** Additional system decorations inset type. */
- public static final int ITYPE_CLIMATE_BAR = 14;
- public static final int ITYPE_EXTRA_NAVIGATION_BAR = 15;
+ public static final int ITYPE_CLIMATE_BAR = 18;
+ public static final int ITYPE_EXTRA_NAVIGATION_BAR = 19;
static final int LAST_TYPE = ITYPE_EXTRA_NAVIGATION_BAR;
public static final int SIZE = LAST_TYPE + 1;
@@ -447,9 +458,11 @@
final ArraySet<Integer> result = new ArraySet<>();
if ((types & Type.STATUS_BARS) != 0) {
result.add(ITYPE_STATUS_BAR);
+ result.add(ITYPE_CLIMATE_BAR);
}
if ((types & Type.NAVIGATION_BARS) != 0) {
result.add(ITYPE_NAVIGATION_BAR);
+ result.add(ITYPE_EXTRA_NAVIGATION_BAR);
}
if ((types & Type.CAPTION_BAR) != 0) {
result.add(ITYPE_CAPTION_BAR);
@@ -485,6 +498,10 @@
return Type.IME;
case ITYPE_TOP_GESTURES:
case ITYPE_BOTTOM_GESTURES:
+ case ITYPE_TOP_MANDATORY_GESTURES:
+ case ITYPE_BOTTOM_MANDATORY_GESTURES:
+ case ITYPE_LEFT_MANDATORY_GESTURES:
+ case ITYPE_RIGHT_MANDATORY_GESTURES:
return Type.MANDATORY_SYSTEM_GESTURES;
case ITYPE_LEFT_GESTURES:
case ITYPE_RIGHT_GESTURES:
@@ -544,6 +561,14 @@
return "ITYPE_LEFT_GESTURES";
case ITYPE_RIGHT_GESTURES:
return "ITYPE_RIGHT_GESTURES";
+ case ITYPE_TOP_MANDATORY_GESTURES:
+ return "ITYPE_TOP_MANDATORY_GESTURES";
+ case ITYPE_BOTTOM_MANDATORY_GESTURES:
+ return "ITYPE_BOTTOM_MANDATORY_GESTURES";
+ case ITYPE_LEFT_MANDATORY_GESTURES:
+ return "ITYPE_LEFT_MANDATORY_GESTURES";
+ case ITYPE_RIGHT_MANDATORY_GESTURES:
+ return "ITYPE_RIGHT_MANDATORY_GESTURES";
case ITYPE_TOP_TAPPABLE_ELEMENT:
return "ITYPE_TOP_TAPPABLE_ELEMENT";
case ITYPE_BOTTOM_TAPPABLE_ELEMENT:
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 8cb8e1d..5b0d950 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -24,6 +24,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo.Translator;
+import android.graphics.BLASTBufferQueue;
import android.graphics.Canvas;
import android.graphics.ColorSpace;
import android.graphics.HardwareRenderer;
@@ -66,6 +67,8 @@
private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);
private static native long nativeGetFromSurfaceControl(long surfaceObject,
long surfaceControlNativeObject);
+ private static native long nativeGetFromBlastBufferQueue(long surfaceObject,
+ long blastBufferQueueNativeObject);
private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
throws OutOfResourcesException;
@@ -534,6 +537,18 @@
}
}
+ private void updateNativeObject(long newNativeObject) {
+ synchronized (mLock) {
+ if (newNativeObject == mNativeObject) {
+ return;
+ }
+ if (mNativeObject != 0) {
+ nativeRelease(mNativeObject);
+ }
+ setNativeObjectLocked(newNativeObject);
+ }
+ }
+
/**
* Copy another surface to this one. This surface now holds a reference
* to the same data as the original surface, and is -not- the owner.
@@ -557,16 +572,27 @@
"null SurfaceControl native object. Are you using a released SurfaceControl?");
}
long newNativeObject = nativeGetFromSurfaceControl(mNativeObject, surfaceControlPtr);
+ updateNativeObject(newNativeObject);
+ }
- synchronized (mLock) {
- if (newNativeObject == mNativeObject) {
- return;
- }
- if (mNativeObject != 0) {
- nativeRelease(mNativeObject);
- }
- setNativeObjectLocked(newNativeObject);
+ /**
+ * Update the surface if the BLASTBufferQueue IGraphicBufferProducer is different from this
+ * surface's IGraphicBufferProducer.
+ *
+ * @param queue {@link BLASTBufferQueue} to copy from.
+ * @hide
+ */
+ public void copyFrom(BLASTBufferQueue queue) {
+ if (queue == null) {
+ throw new IllegalArgumentException("queue must not be null");
}
+
+ long blastBufferQueuePtr = queue.mNativeObject;
+ if (blastBufferQueuePtr == 0) {
+ throw new NullPointerException("Null BLASTBufferQueue native object");
+ }
+ long newNativeObject = nativeGetFromBlastBufferQueue(mNativeObject, blastBufferQueuePtr);
+ updateNativeObject(newNativeObject);
}
/**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d55c25f..c698b92 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -316,11 +316,19 @@
public static final int HIDDEN = 0x00000004;
/**
- * Surface creation flag: The surface contains secure content, special
- * measures will be taken to disallow the surface's content to be copied
- * from another process. In particular, screenshots and VNC servers will
- * be disabled, but other measures can take place, for instance the
- * surface might not be hardware accelerated.
+ * Surface creation flag: Skip this layer and its children when taking a screenshot. This
+ * also includes mirroring and screen recording, so the layers with flag SKIP_SCREENSHOT
+ * will not be included on non primary displays.
+ * @hide
+ */
+ public static final int SKIP_SCREENSHOT = 0x00000040;
+
+ /**
+ * Surface creation flag: Special measures will be taken to disallow the surface's content to
+ * be copied. In particular, screenshots and secondary, non-secure displays will render black
+ * content instead of the surface content.
+ *
+ * @see #createDisplay(String, boolean)
* @hide
*/
public static final int SECURE = 0x00000080;
@@ -482,15 +490,6 @@
public static final int POWER_MODE_ON_SUSPEND = 4;
/**
- * A value for windowType used to indicate that the window should be omitted from screenshots
- * and display mirroring. A temporary workaround until we express such things with
- * the hierarchy.
- * TODO: b/64227542
- * @hide
- */
- public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
-
- /**
* internal representation of how to interpret pixel value, used only to convert to ColorSpace.
*/
private static final int INTERNAL_DATASPACE_SRGB = 142671872;
@@ -3287,6 +3286,22 @@
return this;
}
+ /**
+ * Adds or removes the flag SKIP_SCREENSHOT of the surface. Setting the flag is equivalent
+ * to creating the Surface with the {@link #SKIP_SCREENSHOT} flag.
+ *
+ * @hide
+ */
+ public Transaction setSkipScreenshot(SurfaceControl sc, boolean skipScrenshot) {
+ checkPreconditions(sc);
+ if (skipScrenshot) {
+ nativeSetFlags(mNativeObject, sc.mNativeObject, SKIP_SCREENSHOT, SKIP_SCREENSHOT);
+ } else {
+ nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SKIP_SCREENSHOT);
+ }
+ return this;
+ }
+
/**
* Merge the other transaction into this transaction, clearing the
* other transaction as if it had been applied.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b644e9e..a8ec9ed 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -23,8 +23,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.res.CompatibilityInfo.Translator;
+import android.graphics.BLASTBufferQueue;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -41,6 +43,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceControl.Transaction;
@@ -232,6 +235,19 @@
SurfaceControlViewHost.SurfacePackage mSurfacePackage;
+ /**
+ * Returns {@code true} if buffers should be submitted via blast
+ */
+ private static boolean useBlastAdapter(Context context) {
+ ContentResolver contentResolver = context.getContentResolver();
+ return Settings.Global.getInt(contentResolver,
+ Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV, 0 /* default */) == 1;
+ }
+
+ private final boolean mUseBlastAdapter;
+ private SurfaceControl mBlastSurfaceControl;
+ private BLASTBufferQueue mBlastBufferQueue;
+
public SurfaceView(Context context) {
this(context, null);
}
@@ -252,6 +268,7 @@
public SurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes, boolean disableBackgroundLayer) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mUseBlastAdapter = useBlastAdapter(context);
mRenderNode.addPositionUpdateListener(mPositionListener);
setWillNotDraw(true);
@@ -420,8 +437,6 @@
final boolean useBLAST = viewRoot.useBLAST();
viewRoot.registerRtFrameCallback(frame -> {
try {
- final SurfaceControl.Transaction t = useBLAST ?
- viewRoot.getBLASTSyncTransaction() : new SurfaceControl.Transaction();
synchronized (mSurfaceControlLock) {
if (!parent.isValid()) {
if (DEBUG) {
@@ -443,16 +458,22 @@
Log.d(TAG, System.identityHashCode(this)
+ " updateSurfaceAlpha RT: set alpha=" + alpha);
}
- t.setAlpha(mSurfaceControl, alpha);
- if (!useBLAST) {
+ if (useBLAST) {
+ synchronized (viewRoot.getBlastTransactionLock()) {
+ viewRoot.getBLASTSyncTransaction()
+ .setAlpha(mSurfaceControl, alpha);
+ }
+ } else {
+ Transaction t = new SurfaceControl.Transaction();
+ t.setAlpha(mSurfaceControl, alpha);
t.deferTransactionUntil(mSurfaceControl,
viewRoot.getRenderSurfaceControl(), frame);
+ t.apply();
}
}
// It's possible that mSurfaceControl is released in the UI thread before
// the transaction completes. If that happens, an exception is thrown, which
// must be caught immediately.
- t.apply();
} catch (Exception e) {
Log.e(TAG, System.identityHashCode(this)
+ "updateSurfaceAlpha RT: Exception during surface transaction", e);
@@ -527,7 +548,7 @@
mRequestedVisible = false;
updateSurface();
- releaseSurfaces();
+ tryReleaseSurfaces();
// We don't release this as part of releaseSurfaces as
// that is also called on transient visibility changes. We can't
@@ -793,23 +814,26 @@
final boolean useBLAST = viewRoot.useBLAST();
viewRoot.registerRtFrameCallback(frame -> {
try {
- final SurfaceControl.Transaction t = useBLAST
- ? viewRoot.getBLASTSyncTransaction()
- : new SurfaceControl.Transaction();
synchronized (mSurfaceControlLock) {
if (!parent.isValid() || mSurfaceControl == null) {
return;
}
- updateRelativeZ(t);
- if (!useBLAST) {
+
+ if (useBLAST) {
+ synchronized (viewRoot.getBlastTransactionLock()) {
+ updateRelativeZ(viewRoot.getBLASTSyncTransaction());
+ }
+ } else {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ updateRelativeZ(t);
t.deferTransactionUntil(mSurfaceControl,
viewRoot.getRenderSurfaceControl(), frame);
+ t.apply();
}
}
// It's possible that mSurfaceControl is released in the UI thread before
// the transaction completes. If that happens, an exception is thrown, which
// must be caught immediately.
- t.apply();
} catch (Exception e) {
Log.e(TAG, System.identityHashCode(this)
+ "setZOrderOnTop RT: Exception during surface transaction", e);
@@ -868,7 +892,7 @@
return t;
}
- private void releaseSurfaces() {
+ private void tryReleaseSurfaces() {
mSurfaceAlpha = 1f;
synchronized (mSurfaceControlLock) {
@@ -879,18 +903,30 @@
return;
}
- if (mSurfaceControl != null) {
- mTmpTransaction.remove(mSurfaceControl);
- mSurfaceControl = null;
- }
- if (mBackgroundControl != null) {
- mTmpTransaction.remove(mBackgroundControl);
- mBackgroundControl = null;
- }
+ releaseSurfaces(mTmpTransaction);
mTmpTransaction.apply();
}
}
+ private void releaseSurfaces(Transaction transaction) {
+ if (mSurfaceControl != null) {
+ transaction.remove(mSurfaceControl);
+ mSurfaceControl = null;
+ }
+ if (mBackgroundControl != null) {
+ transaction.remove(mBackgroundControl);
+ mBackgroundControl = null;
+ }
+ if (mBlastSurfaceControl != null) {
+ transaction.remove(mBlastSurfaceControl);
+ mBlastSurfaceControl = null;
+ }
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.destroy();
+ mBlastBufferQueue = null;
+ }
+ }
+
/** @hide */
protected void updateSurface() {
if (!mHaveFrame) {
@@ -907,7 +943,7 @@
if (viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
notifySurfaceDestroyed();
- releaseSurfaces();
+ tryReleaseSurfaces();
return;
}
@@ -972,45 +1008,8 @@
mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
if (creating) {
- mDeferredDestroySurfaceControl = mSurfaceControl;
-
updateOpaqueFlag();
- // SurfaceView hierarchy
- // ViewRootImpl surface
- // - bounds layer (crops all child surfaces to parent surface insets)
- // - SurfaceView surface (drawn relative to ViewRootImpl surface)
- // - Background color layer (drawn behind all SurfaceView surfaces)
- //
- // The bounds layer is used to crop the surface view so it does not draw into
- // the parent surface inset region. Since there can be multiple surface views
- // below or above the parent surface, one option is to create multiple bounds
- // layer for each z order. The other option, the one implement is to create
- // a single bounds layer and set z order for each child surface relative to the
- // parent surface.
- // When creating the surface view, we parent it to the bounds layer and then
- // set the relative z order. When the parent surface changes, we have to
- // make sure to update the relative z via ViewRootImpl.SurfaceChangedCallback.
- final String name = "SurfaceView - " + viewRoot.getTitle().toString();
-
- 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();
-
+ mDeferredDestroySurfaceControl = createSurfaceControls(viewRoot);
} else if (mSurfaceControl == null) {
return;
}
@@ -1083,8 +1082,7 @@
}
mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
if (sizeChanged && !creating) {
- mTmpTransaction.setBufferSize(mSurfaceControl, mSurfaceWidth,
- mSurfaceHeight);
+ setBufferSize(mTmpTransaction);
}
mTmpTransaction.apply();
@@ -1126,19 +1124,7 @@
notifySurfaceDestroyed();
}
- if (creating) {
- mSurface.copyFrom(mSurfaceControl);
- }
-
- if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
- < Build.VERSION_CODES.O) {
- // Some legacy applications use the underlying native {@link Surface} object
- // as a key to whether anything has changed. In these cases, updates to the
- // existing {@link Surface} will be ignored when the size changes.
- // Therefore, we must explicitly recreate the {@link Surface} in these
- // cases.
- mSurface.createFrom(mSurfaceControl);
- }
+ copySurface(creating /* surfaceControlCreated */, sizeChanged);
if (visible && mSurface.isValid()) {
if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
@@ -1182,7 +1168,7 @@
} finally {
mIsCreating = false;
if (mSurfaceControl != null && !mSurfaceCreated) {
- releaseSurfaces();
+ tryReleaseSurfaces();
}
}
} catch (Exception ex) {
@@ -1195,6 +1181,119 @@
}
}
+ /**
+ * Copy the Surface from the SurfaceControl or the blast adapter.
+ *
+ * @param surfaceControlCreated true if we created the SurfaceControl and need to update our
+ * Surface if needed.
+ * @param bufferSizeChanged true if the BufferSize has changed and we need to recreate the
+ * Surface for compatibility reasons.
+ */
+ private void copySurface(boolean surfaceControlCreated, boolean bufferSizeChanged) {
+ if (surfaceControlCreated) {
+ if (mUseBlastAdapter) {
+ mSurface.copyFrom(mBlastBufferQueue);
+ } else {
+ mSurface.copyFrom(mSurfaceControl);
+ }
+ }
+
+ if (bufferSizeChanged && getContext().getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.O) {
+ // Some legacy applications use the underlying native {@link Surface} object
+ // as a key to whether anything has changed. In these cases, updates to the
+ // existing {@link Surface} will be ignored when the size changes.
+ // Therefore, we must explicitly recreate the {@link Surface} in these
+ // cases.
+ if (mUseBlastAdapter) {
+ mSurface.transferFrom(mBlastBufferQueue.createSurface());
+ } else {
+ mSurface.createFrom(mSurfaceControl);
+ }
+ }
+ }
+
+ private void setBufferSize(Transaction transaction) {
+ if (mUseBlastAdapter) {
+ mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
+ } else {
+ transaction.setBufferSize(mSurfaceControl, mSurfaceWidth,
+ mSurfaceHeight);
+ }
+ }
+
+ /**
+ * Creates the surface control hierarchy as follows
+ * ViewRootImpl surface
+ * bounds layer (crops all child surfaces to parent surface insets)
+ * * SurfaceView surface (drawn relative to ViewRootImpl surface)
+ * * Blast surface (if enabled)
+ * * Background color layer (drawn behind all SurfaceView surfaces)
+ *
+ * The bounds layer is used to crop the surface view so it does not draw into the parent
+ * surface inset region. Since there can be multiple surface views below or above the parent
+ * surface, one option is to create multiple bounds layer for each z order. The other option,
+ * the one implement is to create a single bounds layer and set z order for each child surface
+ * relative to the parent surface.
+ * When creating the surface view, we parent it to the bounds layer and then set the relative z
+ * order. When the parent surface changes, we have to make sure to update the relative z via
+ * ViewRootImpl.SurfaceChangedCallback.
+ *
+ * @return previous SurfaceControl where the content was rendered. In the surface is switched
+ * out, the old surface can be persevered until the new one has drawn by keeping the reference
+ * of the old SurfaceControl alive.
+ */
+ private SurfaceControl createSurfaceControls(ViewRootImpl viewRoot) {
+ final String name = "SurfaceView - " + viewRoot.getTitle().toString();
+
+ SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name)
+ .setLocalOwnerView(this)
+ .setParent(viewRoot.getBoundsLayer())
+ .setCallsite("SurfaceView.updateSurface");
+
+ final SurfaceControl previousSurfaceControl;
+ if (mUseBlastAdapter) {
+ mSurfaceControl = builder
+ .setContainerLayer()
+ .build();
+ previousSurfaceControl = mBlastSurfaceControl;
+ mBlastSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName(name + "(BLAST)")
+ .setLocalOwnerView(this)
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFormat(mFormat)
+ .setParent(mSurfaceControl)
+ .setFlags(mSurfaceFlags)
+ .setHidden(false)
+ .setBLASTLayer()
+ .setCallsite("SurfaceView.updateSurface")
+ .build();
+ mBlastBufferQueue = new BLASTBufferQueue(
+ mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, true /* TODO */);
+ } else {
+ previousSurfaceControl = mSurfaceControl;
+ mSurfaceControl = builder
+ .setBufferSize(mSurfaceWidth, mSurfaceHeight)
+ .setFlags(mSurfaceFlags)
+ .setFormat(mFormat)
+ .build();
+ mBlastSurfaceControl = null;
+ mBlastBufferQueue = null;
+ }
+ mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName("Background for " + name)
+ .setLocalOwnerView(this)
+ .setOpaque(true)
+ .setColorLayer()
+ .setParent(mSurfaceControl)
+ .setCallsite("SurfaceView.updateSurface")
+ .build();
+
+ return previousSurfaceControl;
+ }
+
private void onDrawFinished() {
if (DEBUG) {
Log.i(TAG, System.identityHashCode(this) + " "
@@ -1252,7 +1351,7 @@
private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
Rect position, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
- if (frameNumber > 0 && viewRoot != null && !viewRoot.isDrawingToBLASTTransaction()) {
+ if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) {
t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
frameNumber);
}
@@ -1277,19 +1376,23 @@
return mRTLastReportedPosition;
}
+ private void setParentSpaceRectangle(Transaction t, Rect position, long frameNumber) {
+ final ViewRootImpl viewRoot = getViewRootImpl();
+ applySurfaceTransforms(mSurfaceControl, t, position, frameNumber);
+ applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface, frameNumber);
+ }
+
private void setParentSpaceRectangle(Rect position, long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
- final boolean useBLAST = viewRoot.isDrawingToBLASTTransaction();
- final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
- mRtTransaction;
+ final boolean useBLAST = viewRoot.useBLAST();
- applySurfaceTransforms(mSurfaceControl, t, position, frameNumber);
-
- applyChildSurfaceTransaction_renderWorker(t, viewRoot.mSurface,
- frameNumber);
-
- if (!useBLAST) {
- t.apply();
+ if (useBLAST) {
+ synchronized (viewRoot.getBlastTransactionLock()) {
+ setParentSpaceRectangle(viewRoot.getBLASTSyncTransaction(), position, frameNumber);
+ }
+ } else {
+ setParentSpaceRectangle(mRtTransaction, position, frameNumber);
+ mRtTransaction.apply();
}
}
@@ -1340,7 +1443,7 @@
@Override
public void positionLost(long frameNumber) {
final ViewRootImpl viewRoot = getViewRootImpl();
- boolean useBLAST = viewRoot != null && viewRoot.isDrawingToBLASTTransaction();
+ boolean useBLAST = viewRoot != null && viewRoot.useBLAST();
if (DEBUG) {
Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
System.identityHashCode(this), frameNumber));
@@ -1351,38 +1454,37 @@
return;
}
- final SurfaceControl.Transaction t = useBLAST ?
- (viewRoot != null ? viewRoot.getBLASTSyncTransaction() : mRtTransaction) :
- mRtTransaction;
-
/**
* positionLost can be called while UI thread is un-paused so we
* need to hold the lock here.
*/
synchronized (mSurfaceControlLock) {
- if (frameNumber > 0 && viewRoot != null && !useBLAST) {
- if (viewRoot.mSurface.isValid()) {
+ if (useBLAST) {
+ synchronized (viewRoot.getBlastTransactionLock()) {
+ viewRoot.getBLASTSyncTransaction().hide(mSurfaceControl);
+ if (mRtReleaseSurfaces) {
+ mRtReleaseSurfaces = false;
+ releaseSurfaces(viewRoot.getBLASTSyncTransaction());
+ }
+ }
+ } else {
+ if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid()) {
mRtTransaction.deferTransactionUntil(mSurfaceControl,
viewRoot.getRenderSurfaceControl(), frameNumber);
}
- }
- t.hide(mSurfaceControl);
-
- if (mRtReleaseSurfaces) {
- mRtReleaseSurfaces = false;
- mRtTransaction.remove(mSurfaceControl);
- mRtTransaction.remove(mBackgroundControl);
- mSurfaceControl = null;
- mBackgroundControl = null;
+ mRtTransaction.hide(mSurfaceControl);
+ if (mRtReleaseSurfaces) {
+ mRtReleaseSurfaces = false;
+ releaseSurfaces(mRtTransaction);
+ }
+ // If we aren't using BLAST, we apply the transaction locally, otherwise we let
+ // the ViewRoot apply it for us.
+ // If the ViewRoot is null, we behave as if we aren't using BLAST so we need to
+ // apply the transaction.
+ mRtTransaction.apply();
}
mRtHandlingPositionUpdates = false;
}
-
- // If we aren't using BLAST, we apply the transaction locally, otherise we let the ViewRoot apply it for us.
- // If the ViewRoot is null, we behave as if we aren't using BLAST so we need to apply the transaction.
- if (!useBLAST || viewRoot == null) {
- mRtTransaction.apply();
- }
}
};
@@ -1702,7 +1804,6 @@
* @param p The SurfacePackage to embed.
*/
public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
- final SurfaceControl sc = p != null ? p.getSurfaceControl() : null;
final SurfaceControl lastSc = mSurfacePackage != null ?
mSurfacePackage.getSurfaceControl() : null;
if (mSurfaceControl != null && lastSc != null) {
@@ -1719,7 +1820,14 @@
SurfaceControlViewHost.SurfacePackage p) {
initEmbeddedHierarchyForAccessibility(p);
final SurfaceControl sc = p.getSurfaceControl();
- t.reparent(sc, mSurfaceControl).show(sc);
+ final SurfaceControl parent;
+ if (mUseBlastAdapter) {
+ parent = mBlastSurfaceControl;
+ } else {
+ parent = mSurfaceControl;
+ }
+
+ t.reparent(sc, parent).show(sc);
}
/** @hide */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0bbd899..4176e88 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -654,26 +654,25 @@
int localChanges;
}
- // If set, ViewRootImpl will call BLASTBufferQueue::setNextTransaction with
- // mRtBLASTSyncTransaction, prior to invoking draw. This provides a way
- // to redirect the buffers in to transactions.
+ // If set, ViewRootImpl will request a callback from HWRender when it's ready to render the next
+ // frame. This will allow VRI to call BLASTBufferQueue::setNextTransaction with
+ // mRtBLASTSyncTransaction, so the next frame submitted will be added to the
+ // mRtBLASTSyncTransaction instead of getting applied.
private boolean mNextDrawUseBLASTSyncTransaction;
- // Set when calling setNextTransaction, we can't just reuse mNextDrawUseBLASTSyncTransaction
- // because, imagine this scenario:
- // 1. First draw is using BLAST, mNextDrawUseBLAST = true
- // 2. We call perform draw and are waiting on the callback
- // 3. After the first perform draw but before the first callback and the
- // second perform draw, a second draw sets mNextDrawUseBLAST = true (it already was)
- // 4. At this point the callback fires and we set mNextDrawUseBLAST = false;
- // 5. We get to performDraw and fail to sync as we intended because mNextDrawUseBLAST
- // is now false.
- // This is why we use a two-step latch with the two booleans, one consumed from
- // performDraw and one consumed from finishBLASTSync()
- private boolean mNextReportConsumeBLAST;
- // Be very careful with the threading here. This is used from the render thread while
- // the UI thread is paused and then applied and cleared from the UI thread right after
- // draw returns.
- private SurfaceControl.Transaction mRtBLASTSyncTransaction = new SurfaceControl.Transaction();
+
+ // This is used to signal if the mRtBLASTSyncTransaction should be applied/merged. When true,
+ // it indicates mRtBLASTSyncTransaction was sent to BLASTBufferQueue::setNextTransaction.
+ // Therefore, in onFrameComplete, if mRtNextFrameReportConsumeWithBlast is true, that means
+ // mRtBLASTSyncTransaction now contains the next buffer frame to be applied.
+ private boolean mRtNextFrameReportedConsumeWithBlast;
+
+ // Be very careful with the threading here. This is used from a thread pool generated by the
+ // render thread. Therefore, it needs to be locked when updating from the thread pool since
+ // multiple threads can be accessing it. It does not need to be locked when applied or merged
+ // since that can only happen from the frame complete callback after the other callbacks have
+ // been invoked.
+ private final SurfaceControl.Transaction mRtBLASTSyncTransaction =
+ new SurfaceControl.Transaction();
// Keeps track of whether the WM requested us to use BLAST Sync when calling relayout.
// We use this to make sure we don't send the WM transactions from an internal BLAST sync
@@ -1811,7 +1810,7 @@
mBlastSurfaceControl, width, height, mEnableTripleBuffering);
// We only return the Surface the first time, as otherwise
// it hasn't changed and there is no need to update.
- ret = mBlastBufferQueue.getSurface();
+ ret = mBlastBufferQueue.createSurface();
} else {
mBlastBufferQueue.update(mBlastSurfaceControl, width, height);
}
@@ -1986,11 +1985,7 @@
mCompatibleVisibilityInfo.globalVisibility =
(mCompatibleVisibilityInfo.globalVisibility & ~View.SYSTEM_UI_FLAG_LOW_PROFILE)
| (mAttachInfo.mSystemUiVisibility & View.SYSTEM_UI_FLAG_LOW_PROFILE);
- if (mDispatchedSystemUiVisibility != mCompatibleVisibilityInfo.globalVisibility) {
- mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY);
- mHandler.sendMessage(mHandler.obtainMessage(
- MSG_DISPATCH_SYSTEM_UI_VISIBILITY, mCompatibleVisibilityInfo));
- }
+ dispatchDispatchSystemUiVisibilityChanged(mCompatibleVisibilityInfo);
if (mAttachInfo.mKeepScreenOn != oldScreenOn
|| mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
|| mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
@@ -2043,9 +2038,30 @@
info.globalVisibility |= systemUiFlag;
info.localChanges &= ~systemUiFlag;
}
- if (mDispatchedSystemUiVisibility != info.globalVisibility) {
+ dispatchDispatchSystemUiVisibilityChanged(info);
+ }
+
+ /**
+ * If the system is forcing showing any system bar, the legacy low profile flag should be
+ * cleared for compatibility.
+ *
+ * @param showTypes {@link InsetsType types} shown by the system.
+ * @param fromIme {@code true} if the invocation is from IME.
+ */
+ private void clearLowProfileModeIfNeeded(@InsetsType int showTypes, boolean fromIme) {
+ final SystemUiVisibilityInfo info = mCompatibleVisibilityInfo;
+ if ((showTypes & Type.systemBars()) != 0 && !fromIme
+ && (info.globalVisibility & SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
+ info.globalVisibility &= ~SYSTEM_UI_FLAG_LOW_PROFILE;
+ info.localChanges |= SYSTEM_UI_FLAG_LOW_PROFILE;
+ dispatchDispatchSystemUiVisibilityChanged(info);
+ }
+ }
+
+ private void dispatchDispatchSystemUiVisibilityChanged(SystemUiVisibilityInfo args) {
+ if (mDispatchedSystemUiVisibility != args.globalVisibility) {
mHandler.removeMessages(MSG_DISPATCH_SYSTEM_UI_VISIBILITY);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, info));
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
}
}
@@ -2650,7 +2666,7 @@
surfaceSizeChanged = true;
mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
}
- final boolean alwaysConsumeSystemBarsChanged =
+ final boolean alwaysConsumeSystemBarsChanged =
mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars;
updateColorModeIfNeeded(lp.getColorMode());
surfaceCreated = !hadSurface && mSurface.isValid();
@@ -3783,26 +3799,29 @@
commitCallbacks.get(i).run();
}
}
- });});
+ });
+ });
}
}
try {
if (mNextDrawUseBLASTSyncTransaction) {
- // TODO(b/149747443)
- // We aren't prepared to handle overlapping use of mRtBLASTSyncTransaction
- // so if we are BLAST syncing we make sure the previous draw has
- // totally finished.
- if (mAttachInfo.mThreadedRenderer != null) {
- mAttachInfo.mThreadedRenderer.pause();
- }
-
- mNextReportConsumeBLAST = true;
+ // Frame callbacks will always occur after submitting draw requests and before
+ // the draw actually occurs. This will ensure that we set the next transaction
+ // for the frame that's about to get drawn and not on a previous frame that.
+ //
+ // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be
+ // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the
+ // next frame completed should be reported with the blast sync transaction.
+ registerRtFrameCallback(frame -> {
+ mRtNextFrameReportedConsumeWithBlast = true;
+ if (mBlastBufferQueue != null) {
+ // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
+ // being modified and only sent to BlastBufferQueue.
+ mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
+ }
+ });
mNextDrawUseBLASTSyncTransaction = false;
-
- if (mBlastBufferQueue != null) {
- mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
- }
}
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
@@ -4950,6 +4969,7 @@
String.format("Calling showInsets(%d,%b) on window that no longer"
+ " has views.", msg.arg1, msg.arg2 == 1));
}
+ clearLowProfileModeIfNeeded(msg.arg1, msg.arg2 == 1);
mInsetsController.show(msg.arg1, msg.arg2 == 1);
break;
}
@@ -9757,9 +9777,12 @@
private void finishBLASTSync(boolean apply) {
mSendNextFrameToWm = false;
- if (mNextReportConsumeBLAST) {
- mNextReportConsumeBLAST = false;
+ if (mRtNextFrameReportedConsumeWithBlast) {
+ mRtNextFrameReportedConsumeWithBlast = false;
+ // We don't need to synchronize mRtBLASTSyncTransaction here we're guaranteed that this
+ // is called after all onFrameDraw and after callbacks to PositionUpdateListener.
+ // Therefore, no one should be modifying this object until the next vsync.
if (apply) {
mRtBLASTSyncTransaction.apply();
} else {
@@ -9772,6 +9795,10 @@
return mRtBLASTSyncTransaction;
}
+ Object getBlastTransactionLock() {
+ return mRtBLASTSyncTransaction;
+ }
+
/**
* @hide
*/
@@ -9799,12 +9826,4 @@
boolean useBLAST() {
return mUseBLASTAdapter && !mForceDisableBLAST;
}
-
- /**
- * Returns true if we are about to or currently processing a draw directed
- * in to a BLAST transaction.
- */
- boolean isDrawingToBLASTTransaction() {
- return mNextReportConsumeBLAST;
- }
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 8be37e9..ba90154 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -238,6 +238,22 @@
}
/**
+ * Sets whether a container should ignore the orientation request from apps below it. It
+ * currently only applies to {@link com.android.server.wm.TaskDisplayArea}. When {@code false},
+ * it may rotate based on the orientation request; When {@code true}, it can never specify
+ * orientation, but shows the fixed-orientation apps in the letterbox.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setIgnoreOrientationRequest(
+ @NonNull WindowContainerToken container, boolean ignoreOrientationRequest) {
+ Change chg = getOrCreateChange(container.asBinder());
+ chg.mIgnoreOrientationRequest = ignoreOrientationRequest;
+ chg.mChangeMask |= Change.CHANGE_IGNORE_ORIENTATION_REQUEST;
+ return this;
+ }
+
+ /**
* Reparents a container into another one. The effect of a {@code null} parent can vary. For
* example, reparenting a stack to {@code null} will reparent it to its display.
*
@@ -341,10 +357,12 @@
public static final int CHANGE_PIP_CALLBACK = 1 << 2;
public static final int CHANGE_HIDDEN = 1 << 3;
public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
+ public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
private boolean mHidden = false;
+ private boolean mIgnoreOrientationRequest = false;
private int mChangeMask = 0;
private @ActivityInfo.Config int mConfigSetMask = 0;
private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
@@ -362,6 +380,7 @@
mConfiguration.readFromParcel(in);
mFocusable = in.readBoolean();
mHidden = in.readBoolean();
+ mIgnoreOrientationRequest = in.readBoolean();
mChangeMask = in.readInt();
mConfigSetMask = in.readInt();
mWindowSetMask = in.readInt();
@@ -404,6 +423,9 @@
if ((other.mChangeMask & CHANGE_HIDDEN) != 0) {
mHidden = other.mHidden;
}
+ if ((other.mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
+ mIgnoreOrientationRequest = other.mIgnoreOrientationRequest;
+ }
mChangeMask |= other.mChangeMask;
if (other.mActivityWindowingMode >= 0) {
mActivityWindowingMode = other.mActivityWindowingMode;
@@ -445,6 +467,15 @@
return mHidden;
}
+ /** Gets the requested state of whether to ignore orientation request. */
+ public boolean getIgnoreOrientationRequest() {
+ if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) == 0) {
+ throw new RuntimeException("IgnoreOrientationRequest not set. "
+ + "Check CHANGE_IGNORE_ORIENTATION_REQUEST first");
+ }
+ return mIgnoreOrientationRequest;
+ }
+
public int getChangeMask() {
return mChangeMask;
}
@@ -509,6 +540,9 @@
if (mBoundsChangeTransaction != null) {
sb.append("hasBoundsTransaction,");
}
+ if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
+ sb.append("ignoreOrientationRequest:" + mIgnoreOrientationRequest + ",");
+ }
sb.append("}");
return sb.toString();
}
@@ -518,6 +552,7 @@
mConfiguration.writeToParcel(dest, flags);
dest.writeBoolean(mFocusable);
dest.writeBoolean(mHidden);
+ dest.writeBoolean(mIgnoreOrientationRequest);
dest.writeInt(mChangeMask);
dest.writeInt(mConfigSetMask);
dest.writeInt(mWindowSetMask);
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
index f08d0ef8..6b8cf63 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -36,7 +36,7 @@
* (new) system for storing the brightness. It has methods to convert between the two and also
* observes for when one of the settings is changed and syncs this with the other.
*/
-public class BrightnessSynchronizer{
+public class BrightnessSynchronizer {
private static final int MSG_UPDATE_FLOAT = 1;
private static final int MSG_UPDATE_INT = 2;
@@ -78,6 +78,26 @@
mContext = context;
mBrightnessSyncObserver = new BrightnessSyncObserver(mHandler);
mBrightnessSyncObserver.startObserving();
+
+ // It is possible for the system to start up with the int and float values not
+ // synchronized. So we force an update to the int value, since float is the source
+ // of truth. Fallback to int value, if float is invalid. If both are invalid, use default
+ // float value from config.
+ final float currentFloatBrightness = getScreenBrightnessFloat(context);
+ final int currentIntBrightness = getScreenBrightnessInt(context);
+
+ if (!Float.isNaN(currentFloatBrightness)) {
+ updateBrightnessIntFromFloat(currentFloatBrightness);
+ } else if (currentIntBrightness != -1) {
+ updateBrightnessFloatFromInt(currentIntBrightness);
+ } else {
+ final float defaultBrightness = mContext.getResources().getFloat(
+ com.android.internal.R.dimen.config_screenBrightnessSettingDefaultFloat);
+ Settings.System.putFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FLOAT, defaultBrightness,
+ UserHandle.USER_CURRENT);
+
+ }
}
/**
@@ -132,7 +152,8 @@
private static int getScreenBrightnessInt(Context context) {
return Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS, 0, UserHandle.USER_CURRENT);
+ Settings.System.SCREEN_BRIGHTNESS, PowerManager.BRIGHTNESS_INVALID,
+ UserHandle.USER_CURRENT);
}
private float mPreferredSettingValue;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5910b33..944f2ec 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -274,8 +274,6 @@
private int mLastNumberOfChildren = -1;
private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
- // TODO: Update to handle landscape instead of using static value
- private static final int MAX_RANKED_TARGETS = 4;
private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
private final Set<Pair<ComponentName, UserHandle>> mServicesRequested = new HashSet<>();
@@ -952,7 +950,7 @@
updateStickyContentPreview();
if (shouldShowStickyContentPreview()
|| mChooserMultiProfilePagerAdapter
- .getCurrentRootAdapter().getContentPreviewRowCount() != 0) {
+ .getCurrentRootAdapter().getSystemRowCount() != 0) {
logActionShareWithPreview();
}
return postRebuildListInternal(rebuildCompleted);
@@ -1326,13 +1324,14 @@
ViewGroup parent) {
ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
R.layout.chooser_grid_preview_image, parent, false);
+ ViewGroup imagePreview = contentPreviewLayout.findViewById(R.id.content_preview_image_area);
final ViewGroup actionRow =
(ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
//TODO: addActionButton(actionRow, createCopyButton());
addActionButton(actionRow, createNearbyButton(targetIntent));
- mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, true);
+ mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false);
String action = targetIntent.getAction();
if (Intent.ACTION_SEND.equals(action)) {
@@ -1352,7 +1351,7 @@
if (imageUris.size() == 0) {
Log.i(TAG, "Attempted to display image preview area with zero"
+ " available images detected in EXTRA_STREAM list");
- contentPreviewLayout.setVisibility(View.GONE);
+ imagePreview.setVisibility(View.GONE);
return contentPreviewLayout;
}
@@ -2695,7 +2694,7 @@
final int bottomInset = mSystemWindowInsets != null
? mSystemWindowInsets.bottom : 0;
int offset = bottomInset;
- int rowsToShow = gridAdapter.getContentPreviewRowCount()
+ int rowsToShow = gridAdapter.getSystemRowCount()
+ gridAdapter.getProfileRowCount()
+ gridAdapter.getServiceTargetRowCount()
+ gridAdapter.getCallerAndRankedTargetRowCount();
@@ -3295,7 +3294,7 @@
public int getRowCount() {
return (int) (
- getContentPreviewRowCount()
+ getSystemRowCount()
+ getProfileRowCount()
+ getServiceTargetRowCount()
+ getCallerAndRankedTargetRowCount()
@@ -3307,22 +3306,21 @@
}
/**
- * Returns either {@code 0} or {@code 1} depending on whether we want to show the list item
- * content preview. Not to be confused with the sticky content preview which is above the
- * personal and work tabs.
+ * Whether the "system" row of targets is displayed.
+ * This area includes the content preview (if present) and action row.
*/
- public int getContentPreviewRowCount() {
+ public int getSystemRowCount() {
// For the tabbed case we show the sticky content preview above the tabs,
// please refer to shouldShowStickyContentPreview
if (shouldShowTabs()) {
return 0;
}
+
if (!isSendAction(getTargetIntent())) {
return 0;
}
- if (mHideContentPreview || mChooserListAdapter == null
- || mChooserListAdapter.getCount() == 0) {
+ if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) {
return 0;
}
@@ -3364,7 +3362,7 @@
@Override
public int getItemCount() {
return (int) (
- getContentPreviewRowCount()
+ getSystemRowCount()
+ getProfileRowCount()
+ getServiceTargetRowCount()
+ getCallerAndRankedTargetRowCount()
@@ -3419,7 +3417,7 @@
public int getItemViewType(int position) {
int count;
- int countSum = (count = getContentPreviewRowCount());
+ int countSum = (count = getSystemRowCount());
if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
countSum += (count = getProfileRowCount());
@@ -3643,7 +3641,7 @@
}
int getListPosition(int position) {
- position -= getContentPreviewRowCount() + getProfileRowCount();
+ position -= getSystemRowCount() + getProfileRowCount();
final int serviceCount = mChooserListAdapter.getServiceTargetCount();
final int serviceRows = (int) Math.ceil((float) serviceCount
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index dd1978e..85dc2ae 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -24,11 +24,11 @@
import android.view.FrameMetrics;
import android.view.ThreadedRenderer;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor.Session;
import com.android.internal.util.FrameworkStatsLog;
/**
+ * A class that allows the app to get the frame metrics from HardwareRendererObserver.
* @hide
*/
public class FrameTracker implements HardwareRendererObserver.OnFrameMetricsAvailableListener {
@@ -45,28 +45,21 @@
private long mBeginTime = UNKNOWN_TIMESTAMP;
private long mEndTime = UNKNOWN_TIMESTAMP;
private boolean mShouldTriggerTrace;
+ private boolean mSessionEnd;
private int mTotalFramesCount = 0;
private int mMissedFramesCount = 0;
private long mMaxFrameTimeNanos = 0;
private Session mSession;
- public FrameTracker(@NonNull Session session,
- @NonNull Handler handler, @NonNull ThreadedRenderer renderer) {
- mSession = session;
- mRendererWrapper = new ThreadedRendererWrapper(renderer);
- mMetricsWrapper = new FrameMetricsWrapper();
- mObserver = new HardwareRendererObserver(this, mMetricsWrapper.getTiming(), handler);
- }
-
/**
- * This constructor is only for unit tests.
+ * Constructor of FrameTracker.
* @param session a trace session.
- * @param renderer a test double for ThreadedRenderer
- * @param metrics a test double for FrameMetrics
+ * @param handler a handler for handling callbacks.
+ * @param renderer a ThreadedRendererWrapper instance.
+ * @param metrics a FrameMetricsWrapper instance.
*/
- @VisibleForTesting
- public FrameTracker(@NonNull Session session, Handler handler,
+ public FrameTracker(@NonNull Session session, @NonNull Handler handler,
@NonNull ThreadedRendererWrapper renderer, @NonNull FrameMetricsWrapper metrics) {
mSession = session;
mRendererWrapper = renderer;
@@ -77,15 +70,11 @@
/**
* Begin a trace session of the CUJ.
*/
- public void begin() {
+ public synchronized void begin() {
long timestamp = System.nanoTime();
if (DEBUG) {
Log.d(TAG, "begin: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime
- + ", end(ns)=" + mEndTime + ", session=" + mSession);
- }
- if (mBeginTime != UNKNOWN_TIMESTAMP && mEndTime == UNKNOWN_TIMESTAMP) {
- // We have an ongoing tracing already, skip subsequent calls.
- return;
+ + ", end(ns)=" + mEndTime + ", session=" + mSession.getName());
}
mBeginTime = timestamp;
mEndTime = UNKNOWN_TIMESTAMP;
@@ -96,32 +85,48 @@
/**
* End the trace session of the CUJ.
*/
- public void end() {
+ public synchronized void end() {
long timestamp = System.nanoTime();
if (DEBUG) {
Log.d(TAG, "end: time(ns)=" + timestamp + ", begin(ns)=" + mBeginTime
- + ", end(ns)=" + mEndTime + ", session=" + mSession);
- }
- if (mBeginTime == UNKNOWN_TIMESTAMP || mEndTime != UNKNOWN_TIMESTAMP) {
- // We haven't started a trace yet.
- return;
+ + ", end(ns)=" + mEndTime + ", session=" + mSession.getName());
}
mEndTime = timestamp;
Trace.endAsyncSection(mSession.getName(), (int) mBeginTime);
+ // We don't remove observer here,
+ // will remove it when all the frame metrics in this duration are called back.
+ // See onFrameMetricsAvailable for the logic of removing the observer.
+ }
+
+ /**
+ * Cancel the trace session of the CUJ.
+ */
+ public synchronized void cancel() {
+ if (mBeginTime == UNKNOWN_TIMESTAMP || mEndTime != UNKNOWN_TIMESTAMP) return;
+ if (DEBUG) {
+ Log.d(TAG, "cancel: time(ns)=" + System.nanoTime() + ", begin(ns)=" + mBeginTime
+ + ", end(ns)=" + mEndTime + ", session=" + mSession.getName());
+ }
+ Trace.endAsyncSection(mSession.getName(), (int) mBeginTime);
+ mRendererWrapper.removeObserver(mObserver);
+ mBeginTime = UNKNOWN_TIMESTAMP;
+ mEndTime = UNKNOWN_TIMESTAMP;
+ mShouldTriggerTrace = false;
}
@Override
- public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
+ public synchronized void onFrameMetricsAvailable(int dropCountSinceLastInvocation) {
// Since this callback might come a little bit late after the end() call.
// We should keep tracking the begin / end timestamp.
// Then compare with vsync timestamp to check if the frame is in the duration of the CUJ.
- if (mBeginTime == UNKNOWN_TIMESTAMP) return; // We haven't started tracing yet.
long vsyncTimestamp = mMetricsWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP);
- if (vsyncTimestamp < mBeginTime) return; // The tracing has been started.
+ // Discard the frame metrics which is not in the trace session.
+ if (vsyncTimestamp < mBeginTime) return;
- // If the end time has not been set, we are still in the tracing.
- if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime) {
+ // We stop getting callback when the vsync is later than the end calls.
+ if (mEndTime != UNKNOWN_TIMESTAMP && vsyncTimestamp > mEndTime && !mSessionEnd) {
+ mSessionEnd = true;
// The tracing has been ended, remove the observer, see if need to trigger perfetto.
mRendererWrapper.removeObserver(mObserver);
@@ -170,9 +175,8 @@
/**
* Trigger the prefetto daemon.
*/
- @VisibleForTesting
public void triggerPerfetto() {
- InteractionJankMonitor.trigger();
+ InteractionJankMonitor.getInstance().trigger();
}
/**
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 5a0cbf9..19dc2ed 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -21,15 +21,17 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.HandlerThread;
-import android.view.ThreadedRenderer;
+import android.util.Log;
+import android.util.SparseArray;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
+import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* This class let users to begin and end the always on tracing mechanism.
@@ -38,11 +40,17 @@
public class InteractionJankMonitor {
private static final String TAG = InteractionJankMonitor.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final Object LOCK = new Object();
+ private static final String DEFAULT_WORKER_NAME = TAG + "-Worker";
+ private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5L);
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
- public static final int CUJ_NOTIFICATION_SHADE_MOTION = 0;
- public static final int CUJ_NOTIFICATION_SHADE_GESTURE = 1;
+ public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 1;
+ public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE = 0;
+ public static final int CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE = 0;
private static final int NO_STATSD_LOGGING = -1;
@@ -53,141 +61,255 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
};
- private static ThreadedRenderer sRenderer;
- private static Map<String, FrameTracker> sRunningTracker;
- private static HandlerThread sWorker;
- private static boolean sInitialized;
+ private static volatile InteractionJankMonitor sInstance;
+
+ private ThreadedRendererWrapper mRenderer;
+ private FrameMetricsWrapper mMetrics;
+ private SparseArray<FrameTracker> mRunningTrackers;
+ private SparseArray<Runnable> mTimeoutActions;
+ private HandlerThread mWorker;
+ private boolean mInitialized;
/** @hide */
@IntDef({
- CUJ_NOTIFICATION_SHADE_MOTION,
- CUJ_NOTIFICATION_SHADE_GESTURE
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+ CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK,
+ CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
+ CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
+ CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
+ CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+ CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {}
/**
- * @param view Any view in the view tree to get context and ThreadedRenderer.
+ * Get the singleton of InteractionJankMonitor.
+ * @return instance of InteractionJankMonitor
*/
- public static void init(@NonNull View view) {
- init(view, null, null, null);
- }
-
- /**
- * Should be only invoked internally or from unit tests.
- */
- @VisibleForTesting
- public static void init(@NonNull View view, @NonNull ThreadedRenderer renderer,
- @NonNull Map<String, FrameTracker> map, @NonNull HandlerThread worker) {
- //TODO (163505250): This should be no-op if not in droid food rom.
- synchronized (LOCK) {
- if (!sInitialized) {
- if (!view.isAttachedToWindow()) {
- throw new IllegalStateException("View is not attached!");
+ public static InteractionJankMonitor getInstance() {
+ // Use DCL here since this method might be invoked very often.
+ if (sInstance == null) {
+ synchronized (InteractionJankMonitor.class) {
+ if (sInstance == null) {
+ sInstance = new InteractionJankMonitor(new HandlerThread(DEFAULT_WORKER_NAME));
}
- sRenderer = renderer == null ? view.getThreadedRenderer() : renderer;
- sRunningTracker = map == null ? new HashMap<>() : map;
- sWorker = worker == null ? new HandlerThread("Aot-Worker") : worker;
- sWorker.start();
- sInitialized = true;
}
}
+ return sInstance;
+ }
+
+ /**
+ * This constructor should be only public to tests.
+ * @param worker the worker thread for the callbacks
+ */
+ @VisibleForTesting
+ public InteractionJankMonitor(@NonNull HandlerThread worker) {
+ mRunningTrackers = new SparseArray<>();
+ mTimeoutActions = new SparseArray<>();
+ mWorker = worker;
+ }
+
+ /**
+ * Init InteractionJankMonitor for later instrumentation.
+ * @param view Any view in the view tree to get context and ThreadedRenderer.
+ * @return boolean true if the instance has been initialized successfully.
+ */
+ public boolean init(@NonNull View view) {
+ //TODO (163505250): This should be no-op if not in droid food rom.
+ if (!mInitialized) {
+ synchronized (this) {
+ if (!mInitialized) {
+ if (!view.isAttachedToWindow()) {
+ Log.d(TAG, "Expect an attached view!", new Throwable());
+ return false;
+ }
+ mRenderer = new ThreadedRendererWrapper(view.getThreadedRenderer());
+ mMetrics = new FrameMetricsWrapper();
+ mWorker.start();
+ mInitialized = true;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Create a {@link FrameTracker} instance.
+ * @param session the session associates with this tracker
+ * @return instance of the FrameTracker
+ */
+ @VisibleForTesting
+ public FrameTracker createFrameTracker(Session session) {
+ synchronized (this) {
+ if (!mInitialized) return null;
+ return new FrameTracker(session, mWorker.getThreadHandler(), mRenderer, mMetrics);
+ }
}
/**
- * Must invoke init() before invoking this method.
+ * Begin a trace session, must invoke {@link #init(View)} before invoking this method.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @return boolean true if the tracker is started successfully, false otherwise.
*/
- public static void begin(@NonNull @CujType int cujType) {
- begin(cujType, null);
+ public boolean begin(@CujType int cujType) {
+ //TODO (163505250): This should be no-op if not in droid food rom.
+ synchronized (this) {
+ return begin(cujType, 0L /* timeout */);
+ }
}
/**
- * Should be only invoked internally or from unit tests.
+ * Begin a trace session, must invoke {@link #init(View)} before invoking this method.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @param timeout the elapsed time in ms until firing the timeout action.
+ * @return boolean true if the tracker is started successfully, false otherwise.
*/
- @VisibleForTesting
- public static void begin(@NonNull @CujType int cujType, FrameTracker tracker) {
+ public boolean begin(@CujType int cujType, long timeout) {
//TODO (163505250): This should be no-op if not in droid food rom.
- //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads.
- synchronized (LOCK) {
- checkInitStateLocked();
- Session session = new Session(cujType);
- FrameTracker currentTracker = getTracker(session.getName());
- if (currentTracker != null) return;
- if (tracker == null) {
- tracker = new FrameTracker(session, sWorker.getThreadHandler(), sRenderer);
+ synchronized (this) {
+ if (!mInitialized) {
+ Log.d(TAG, "Not initialized!", new Throwable());
+ return false;
}
- sRunningTracker.put(session.getName(), tracker);
+ Session session = new Session(cujType);
+ FrameTracker tracker = getTracker(session);
+ // Skip subsequent calls if we already have an ongoing tracing.
+ if (tracker != null) return false;
+
+ // begin a new trace session.
+ tracker = createFrameTracker(session);
+ mRunningTrackers.put(cujType, tracker);
tracker.begin();
+
+ // Cancel the trace if we don't get an end() call in specified duration.
+ timeout = timeout > 0L ? timeout : DEFAULT_TIMEOUT_MS;
+ Runnable timeoutAction = () -> cancel(cujType);
+ mTimeoutActions.put(cujType, timeoutAction);
+ mWorker.getThreadHandler().postDelayed(timeoutAction, timeout);
+ return true;
}
}
/**
- * Must invoke init() before invoking this method.
+ * End a trace session, must invoke {@link #init(View)} before invoking this method.
+ * @param cujType the specific {@link InteractionJankMonitor.CujType}.
+ * @return boolean true if the tracker is ended successfully, false otherwise.
*/
- public static void end(@NonNull @CujType int cujType) {
+ public boolean end(@CujType int cujType) {
//TODO (163505250): This should be no-op if not in droid food rom.
- //TODO (163510843): Remove synchronized, add @UiThread if only invoked from ui threads.
- synchronized (LOCK) {
- checkInitStateLocked();
- Session session = new Session(cujType);
- FrameTracker tracker = getTracker(session.getName());
- if (tracker != null) {
- tracker.end();
- sRunningTracker.remove(session.getName());
+ synchronized (this) {
+ if (!mInitialized) {
+ Log.d(TAG, "Not initialized!", new Throwable());
+ return false;
}
- }
- }
+ // remove the timeout action first.
+ Runnable timeout = mTimeoutActions.get(cujType);
+ if (timeout != null) {
+ mWorker.getThreadHandler().removeCallbacks(timeout);
+ mTimeoutActions.remove(cujType);
+ }
- private static void checkInitStateLocked() {
- if (!sInitialized) {
- throw new IllegalStateException("InteractionJankMonitor not initialized!");
+ Session session = new Session(cujType);
+ FrameTracker tracker = getTracker(session);
+ // Skip this call since we haven't started a trace yet.
+ if (tracker == null) return false;
+ tracker.end();
+ mRunningTrackers.remove(session.getCuj());
+ return true;
}
}
/**
- * Should be only invoked from unit tests.
+ * Cancel the trace session, must invoke {@link #init(View)} before invoking this method.
+ * @return boolean true if the tracker is cancelled successfully, false otherwise.
+ */
+ public boolean cancel(@CujType int cujType) {
+ //TODO (163505250): This should be no-op if not in droid food rom.
+ synchronized (this) {
+ if (!mInitialized) {
+ Log.d(TAG, "Not initialized!", new Throwable());
+ return false;
+ }
+ // remove the timeout action first.
+ Runnable timeout = mTimeoutActions.get(cujType);
+ if (timeout != null) {
+ mWorker.getThreadHandler().removeCallbacks(timeout);
+ mTimeoutActions.remove(cujType);
+ }
+
+ Session session = new Session(cujType);
+ FrameTracker tracker = getTracker(session);
+ // Skip this call since we haven't started a trace yet.
+ if (tracker == null) return false;
+ tracker.cancel();
+ mRunningTrackers.remove(session.getCuj());
+ return true;
+ }
+ }
+
+ private void destroy() {
+ synchronized (this) {
+ int trackers = mRunningTrackers.size();
+ for (int i = 0; i < trackers; i++) {
+ mRunningTrackers.valueAt(i).cancel();
+ }
+ mRunningTrackers = null;
+ mTimeoutActions.clear();
+ mTimeoutActions = null;
+ mWorker.quit();
+ mWorker = null;
+ }
+ }
+
+ /**
+ * Abandon current instance.
*/
@VisibleForTesting
- public static void reset() {
- sInitialized = false;
- sRenderer = null;
- sRunningTracker = null;
- if (sWorker != null) {
- sWorker.quit();
- sWorker = null;
+ public static void abandon() {
+ if (sInstance == null) return;
+ synchronized (InteractionJankMonitor.class) {
+ if (sInstance == null) return;
+ sInstance.destroy();
+ sInstance = null;
}
}
- private static FrameTracker getTracker(String sessionName) {
- synchronized (LOCK) {
- return sRunningTracker.get(sessionName);
+ private FrameTracker getTracker(Session session) {
+ synchronized (this) {
+ if (!mInitialized) return null;
+ return mRunningTrackers.get(session.getCuj());
}
}
/**
* Trigger the perfetto daemon to collect and upload data.
*/
- public static void trigger() {
- sWorker.getThreadHandler().post(
- () -> PerfettoTrigger.trigger(PerfettoTrigger.TRIGGER_TYPE_JANK));
+ @VisibleForTesting
+ public void trigger() {
+ synchronized (this) {
+ if (!mInitialized) return;
+ mWorker.getThreadHandler().post(
+ () -> PerfettoTrigger.trigger(PerfettoTrigger.TRIGGER_TYPE_JANK));
+ }
}
/**
* A class to represent a session.
*/
public static class Session {
- private @CujType int mId;
+ private @CujType int mCujType;
- public Session(@CujType int session) {
- mId = session;
+ public Session(@CujType int cujType) {
+ mCujType = cujType;
}
- public int getId() {
- return mId;
+ public int getCuj() {
+ return mCujType;
}
public int getStatsdInteractionType() {
- return CUJ_TO_STATSD_INTERACTION_TYPE[mId];
+ return CUJ_TO_STATSD_INTERACTION_TYPE[mCujType];
}
/** Describes whether the measurement from this session should be written to statsd. */
@@ -196,7 +318,7 @@
}
public String getName() {
- return "CujType<" + mId + ">";
+ return "Cuj<" + getCuj() + ">";
}
}
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index c9156c1..c0f44a5 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -33,7 +33,11 @@
public void log(UiEventEnum event, int uid, String packageName) {
final int eventID = event.getId();
if (eventID > 0) {
- FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED, eventID, uid, packageName);
+ FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED,
+ /* event_id = 1 */ eventID,
+ /* uid = 2 */ uid,
+ /* package_name = 3 */ packageName,
+ /* instance_id = 4 */ 0);
}
}
@@ -42,8 +46,11 @@
InstanceId instance) {
final int eventID = event.getId();
if ((eventID > 0) && (instance != null)) {
- FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED, eventID, uid, packageName,
- instance.getId());
+ FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED,
+ /* event_id = 1 */ eventID,
+ /* uid = 2 */ uid,
+ /* package_name = 3 */ packageName,
+ /* instance_id = 4 */ instance.getId());
} else {
log(event, uid, packageName);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e58990e..4a0e26a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -156,7 +156,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0);
+ static final int VERSION = 188 + (USE_OLD_HISTORY ? 1000 : 0);
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -6613,22 +6613,29 @@
* the power consumption to the calling app.
*/
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
- noteBinderCallStats(workSourceUid, incrementalCallCount, callStats, binderThreadNativeTids,
+ Collection<BinderCallsStats.CallStat> callStats) {
+ noteBinderCallStats(workSourceUid, incrementalCallCount, callStats,
mClocks.elapsedRealtime(), mClocks.uptimeMillis());
}
public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids,
+ Collection<BinderCallsStats.CallStat> callStats,
long elapsedRealtimeMs, long uptimeMs) {
synchronized (this) {
getUidStatsLocked(workSourceUid, elapsedRealtimeMs, uptimeMs)
.noteBinderCallStatsLocked(incrementalCallCount, callStats);
- mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
}
}
/**
+ * Takes note of native IDs of threads taking incoming binder calls. The CPU time
+ * of these threads is attributed to the apps making those binder calls.
+ */
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
+ }
+
+ /**
* Estimates the proportion of system server CPU activity handling incoming binder calls
* that can be attributed to each app
*/
@@ -14569,6 +14576,7 @@
mDailyStartTimeMs = in.readLong();
mNextMinDailyDeadlineMs = in.readLong();
mNextMaxDailyDeadlineMs = in.readLong();
+ mBatteryTimeToFullSeconds = in.readLong();
mStartCount++;
@@ -15061,6 +15069,7 @@
out.writeLong(mDailyStartTimeMs);
out.writeLong(mNextMinDailyDeadlineMs);
out.writeLong(mNextMaxDailyDeadlineMs);
+ out.writeLong(mBatteryTimeToFullSeconds);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -15644,6 +15653,7 @@
mDischargeLightDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mDischargeDeepDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mLastWriteTimeMs = in.readLong();
+ mBatteryTimeToFullSeconds = in.readLong();
mRpmStats.clear();
int NRPMS = in.readInt();
@@ -15843,6 +15853,7 @@
mDischargeLightDozeCounter.writeToParcel(out);
mDischargeDeepDozeCounter.writeToParcel(out);
out.writeLong(mLastWriteTimeMs);
+ out.writeLong(mBatteryTimeToFullSeconds);
out.writeInt(mRpmStats.size());
for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index f5bef0b..70b1ad4 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -119,8 +119,8 @@
if (uidEntry != null) {
ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
- uidEntry.incrementalCallCount, callStats.values(),
- mNativeTids.toArray());
+ uidEntry.incrementalCallCount, callStats.values()
+ );
uidEntry.incrementalCallCount = 0;
for (int j = callStats.size() - 1; j >= 0; j--) {
callStats.valueAt(j).incrementalCallCount = 0;
@@ -168,6 +168,7 @@
public void setCallStatsObserver(
BinderInternal.CallStatsObserver callStatsObserver) {
mCallStatsObserver = callStatsObserver;
+ noteBinderThreadNativeIds();
noteCallsStatsDelayed();
}
@@ -182,13 +183,13 @@
@Override
@Nullable
public CallSession callStarted(Binder binder, int code, int workSourceUid) {
+ noteNativeThreadId();
+
if (!mRecordingAllTransactionsForUid
&& (mDeviceState == null || mDeviceState.isCharging())) {
return null;
}
- noteNativeThreadId();
-
final CallSession s = obtainCallSession();
s.binderClass = binder.getClass();
s.transactionCode = code;
@@ -359,6 +360,16 @@
mNativeTids = copyOnWriteArray;
}
}
+
+ noteBinderThreadNativeIds();
+ }
+
+ private void noteBinderThreadNativeIds() {
+ if (mCallStatsObserver == null) {
+ return;
+ }
+
+ mCallStatsObserver.noteBinderThreadNativeIds(getNativeTids());
}
/**
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 2645b8e..c14d8d8 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -143,8 +143,12 @@
* Notes incoming binder call stats associated with this work source UID.
*/
void noteCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats,
- int[] binderThreadNativeTids);
+ Collection<BinderCallsStats.CallStat> callStats);
+
+ /**
+ * Notes the native IDs of threads taking incoming binder calls.
+ */
+ void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
}
/**
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index 1cdd42c..3aa2390 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -31,7 +31,7 @@
*/
public class SystemServerCpuThreadReader {
private KernelCpuThreadReader mKernelCpuThreadReader;
- private int[] mBinderThreadNativeTids;
+ private int[] mBinderThreadNativeTids = new int[0]; // Sorted
private int[] mThreadCpuTimesUs;
private int[] mBinderThreadCpuTimesUs;
@@ -75,7 +75,8 @@
}
public void setBinderThreadNativeTids(int[] nativeTids) {
- mBinderThreadNativeTids = nativeTids;
+ mBinderThreadNativeTids = nativeTids.clone();
+ Arrays.sort(mBinderThreadNativeTids);
}
/**
@@ -107,7 +108,8 @@
int threadCpuUsagesSize = threadCpuUsages.size();
for (int j = 0; j < threadCpuUsagesSize; j++) {
KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j);
- boolean isBinderThread = isBinderThread(tcu.threadId);
+ boolean isBinderThread =
+ Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0;
final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length);
for (int k = 0; k < len; k++) {
@@ -138,14 +140,4 @@
return mDeltaCpuThreadTimes;
}
- private boolean isBinderThread(int threadId) {
- if (mBinderThreadNativeTids != null) {
- for (int i = 0; i < mBinderThreadNativeTids.length; i++) {
- if (threadId == mBinderThreadNativeTids[i]) {
- return true;
- }
- }
- }
- return false;
- }
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 1f8a829..4512fba 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1105,15 +1105,18 @@
: controller.getSystemBarsAppearance();
if (insets != null) {
- final Insets systemBarInsets = insets.getInsets(WindowInsets.Type.systemBars());
- final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
- WindowInsets.Type.systemBars());
final boolean clearCompatInsets = clearCompatInsets(attrs.type, attrs.flags,
getResources().getConfiguration().windowConfiguration.getWindowingMode());
- mLastTopInset = clearCompatInsets ? 0 : systemBarInsets.top;
- mLastBottomInset = clearCompatInsets ? 0 : systemBarInsets.bottom;
- mLastRightInset = clearCompatInsets ? 0 : systemBarInsets.right;
- mLastLeftInset = clearCompatInsets ? 0 : systemBarInsets.left;
+ final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.systemBars());
+ final Insets systemInsets = clearCompatInsets
+ ? Insets.NONE
+ : Insets.min(insets.getInsets(WindowInsets.Type.systemBars()
+ | WindowInsets.Type.displayCutout()), stableBarInsets);
+ mLastTopInset = systemInsets.top;
+ mLastBottomInset = systemInsets.bottom;
+ mLastRightInset = systemInsets.right;
+ mLastLeftInset = systemInsets.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
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 9f7436a..9874c6a 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -72,7 +72,9 @@
Consts.TAG_WM),
WM_DEBUG_WINDOW_ORGANIZER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM),
- TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
+ WM_DEBUG_SYNC_ENGINE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM),
+ TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
private final boolean mEnabled;
private volatile boolean mLogToProto;
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 6b754ca..396a84f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -1520,7 +1520,12 @@
readPublicLibrariesListFile(new File("/vendor/etc/public.libraries.txt"));
String[] dirs = {"/system/etc", "/system_ext/etc", "/product/etc"};
for (String dir : dirs) {
- for (File f : (new File(dir)).listFiles()) {
+ File[] files = new File(dir).listFiles();
+ if (files == null) {
+ Slog.w(TAG, "Public libraries file folder missing: " + dir);
+ continue;
+ }
+ for (File f : files) {
String name = f.getName();
if (name.startsWith("public.libraries-") && name.endsWith(".txt")) {
readPublicLibrariesListFile(f);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index ef0eeec..5a859aa 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -792,7 +792,7 @@
}
static jlong android_os_Debug_getIonHeapsSizeKb(JNIEnv* env, jobject clazz) {
- jlong heapsSizeKb = 0;
+ jlong heapsSizeKb = -1;
uint64_t size;
if (meminfo::ReadIonHeapsSizeKb(&size)) {
@@ -803,7 +803,7 @@
}
static jlong android_os_Debug_getIonPoolsSizeKb(JNIEnv* env, jobject clazz) {
- jlong poolsSizeKb = 0;
+ jlong poolsSizeKb = -1;
uint64_t size;
if (meminfo::ReadIonPoolsSizeKb(&size)) {
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 2436b23..d4a7462 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -36,6 +36,9 @@
static struct {
jclass clazz;
+ jmethodID mCtor;
+ jmethodID mSetNativeInputChannel;
+
jfieldID mPtr; // native object attached to the DVM InputChannel
} gInputChannelClassInfo;
@@ -43,7 +46,7 @@
class NativeInputChannel {
public:
- explicit NativeInputChannel(const std::shared_ptr<InputChannel>& inputChannel);
+ explicit NativeInputChannel(std::unique_ptr<InputChannel> inputChannel);
~NativeInputChannel();
inline std::shared_ptr<InputChannel> getInputChannel() { return mInputChannel; }
@@ -59,8 +62,8 @@
// ----------------------------------------------------------------------------
-NativeInputChannel::NativeInputChannel(const std::shared_ptr<InputChannel>& inputChannel)
- : mInputChannel(inputChannel), mDisposeCallback(nullptr) {}
+NativeInputChannel::NativeInputChannel(std::unique_ptr<InputChannel> inputChannel)
+ : mInputChannel(std::move(inputChannel)), mDisposeCallback(nullptr) {}
NativeInputChannel::~NativeInputChannel() {
}
@@ -110,13 +113,33 @@
}
static jlong android_view_InputChannel_createInputChannel(
- JNIEnv* env, std::shared_ptr<InputChannel> inputChannel) {
+ JNIEnv* env, std::unique_ptr<InputChannel> inputChannel) {
std::unique_ptr<NativeInputChannel> nativeInputChannel =
- std::make_unique<NativeInputChannel>(inputChannel);
+ std::make_unique<NativeInputChannel>(std::move(inputChannel));
return reinterpret_cast<jlong>(nativeInputChannel.release());
}
+jobject android_view_InputChannel_createJavaObject(JNIEnv* env,
+ std::unique_ptr<InputChannel> inputChannel) {
+ std::string name = inputChannel->getName();
+ jlong ptr = android_view_InputChannel_createInputChannel(env, std::move(inputChannel));
+ jobject javaInputChannel =
+ env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.mCtor);
+ if (!javaInputChannel) {
+ ALOGE("Failed to create a Java InputChannel for channel %s.", name.c_str());
+ return nullptr;
+ }
+
+ env->CallVoidMethod(javaInputChannel, gInputChannelClassInfo.mSetNativeInputChannel, ptr);
+ if (env->ExceptionOccurred()) {
+ ALOGE("Failed to set native ptr to the Java InputChannel for channel %s.",
+ inputChannel->getName().c_str());
+ return nullptr;
+ }
+ return javaInputChannel;
+}
+
static jlongArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
ScopedUtfChars nameChars(env, nameObj);
@@ -180,9 +203,10 @@
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
- std::shared_ptr<InputChannel> inputChannel = std::make_shared<InputChannel>();
+ std::unique_ptr<InputChannel> inputChannel = std::make_unique<InputChannel>();
inputChannel->readFromParcel(parcel);
- NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
+ NativeInputChannel* nativeInputChannel =
+ new NativeInputChannel(std::move(inputChannel));
return reinterpret_cast<jlong>(nativeInputChannel);
}
}
@@ -233,13 +257,13 @@
return 0;
}
- std::shared_ptr<InputChannel> dupInputChannel = inputChannel->dup();
+ std::unique_ptr<InputChannel> dupInputChannel = inputChannel->dup();
if (dupInputChannel == nullptr) {
std::string message = android::base::StringPrintf(
"Could not duplicate input channel %s", inputChannel->getName().c_str());
jniThrowRuntimeException(env, message.c_str());
}
- return reinterpret_cast<jlong>(new NativeInputChannel(dupInputChannel));
+ return reinterpret_cast<jlong>(new NativeInputChannel(std::move(dupInputChannel)));
}
static jobject android_view_InputChannel_nativeGetToken(JNIEnv* env, jobject obj, jlong channel) {
@@ -281,6 +305,11 @@
jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
+ gInputChannelClassInfo.mCtor =
+ GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>", "()V");
+ gInputChannelClassInfo.mSetNativeInputChannel =
+ GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "setNativeInputChannel", "(J)V");
+
gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");
return res;
diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h
index 8030c96..50b9aed 100644
--- a/core/jni/android_view_InputChannel.h
+++ b/core/jni/android_view_InputChannel.h
@@ -36,6 +36,8 @@
extern void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChannelObj,
InputChannelObjDisposeCallback callback, void* data = NULL);
+extern jobject android_view_InputChannel_createJavaObject(
+ JNIEnv* env, std::unique_ptr<InputChannel> inputChannel);
} // namespace android
#endif // _ANDROID_OS_INPUTCHANNEL_H
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 4cfc205b..6a07cf7 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -32,9 +32,10 @@
#include "android_os_Parcel.h"
#include <binder/Parcel.h>
+#include <gui/BLASTBufferQueue.h>
#include <gui/Surface.h>
-#include <gui/view/Surface.h>
#include <gui/SurfaceControl.h>
+#include <gui/view/Surface.h>
#include <ui/GraphicBuffer.h>
#include <ui/Rect.h>
@@ -300,6 +301,26 @@
return reinterpret_cast<jlong>(surface.get());
}
+static jlong nativeGetFromBlastBufferQueue(JNIEnv* env, jclass clazz, jlong nativeObject,
+ jlong blastBufferQueueNativeObj) {
+ Surface* self(reinterpret_cast<Surface*>(nativeObject));
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(blastBufferQueueNativeObj);
+ const sp<IGraphicBufferProducer>& bufferProducer = queue->getIGraphicBufferProducer();
+ // If the underlying IGBP's are the same, we don't need to do anything.
+ if (self != nullptr &&
+ IInterface::asBinder(self->getIGraphicBufferProducer()) ==
+ IInterface::asBinder(bufferProducer)) {
+ return nativeObject;
+ }
+
+ sp<Surface> surface(new Surface(bufferProducer, true));
+ if (surface != NULL) {
+ surface->incStrong(&sRefBaseOwner);
+ }
+
+ return reinterpret_cast<jlong>(surface.get());
+}
+
static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
@@ -428,38 +449,31 @@
// ----------------------------------------------------------------------------
static const JNINativeMethod gSurfaceMethods[] = {
- {"nativeCreateFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)J",
- (void*)nativeCreateFromSurfaceTexture },
- {"nativeRelease", "(J)V",
- (void*)nativeRelease },
- {"nativeIsValid", "(J)Z",
- (void*)nativeIsValid },
- {"nativeIsConsumerRunningBehind", "(J)Z",
- (void*)nativeIsConsumerRunningBehind },
- {"nativeLockCanvas", "(JLandroid/graphics/Canvas;Landroid/graphics/Rect;)J",
- (void*)nativeLockCanvas },
- {"nativeUnlockCanvasAndPost", "(JLandroid/graphics/Canvas;)V",
- (void*)nativeUnlockCanvasAndPost },
- {"nativeAllocateBuffers", "(J)V",
- (void*)nativeAllocateBuffers },
- {"nativeCreateFromSurfaceControl", "(J)J",
- (void*)nativeCreateFromSurfaceControl },
- {"nativeGetFromSurfaceControl", "(JJ)J",
- (void*)nativeGetFromSurfaceControl },
- {"nativeReadFromParcel", "(JLandroid/os/Parcel;)J",
- (void*)nativeReadFromParcel },
- {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
- (void*)nativeWriteToParcel },
- {"nativeGetWidth", "(J)I", (void*)nativeGetWidth },
- {"nativeGetHeight", "(J)I", (void*)nativeGetHeight },
- {"nativeGetNextFrameNumber", "(J)J", (void*)nativeGetNextFrameNumber },
- {"nativeSetScalingMode", "(JI)I", (void*)nativeSetScalingMode },
- {"nativeForceScopedDisconnect", "(J)I", (void*)nativeForceScopedDisconnect},
- {"nativeAttachAndQueueBufferWithColorSpace", "(JLandroid/hardware/HardwareBuffer;I)I",
- (void*)nativeAttachAndQueueBufferWithColorSpace},
- {"nativeSetSharedBufferModeEnabled", "(JZ)I", (void*)nativeSetSharedBufferModeEnabled},
- {"nativeSetAutoRefreshEnabled", "(JZ)I", (void*)nativeSetAutoRefreshEnabled},
- {"nativeSetFrameRate", "(JFI)I", (void*)nativeSetFrameRate},
+ {"nativeCreateFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)J",
+ (void*)nativeCreateFromSurfaceTexture},
+ {"nativeRelease", "(J)V", (void*)nativeRelease},
+ {"nativeIsValid", "(J)Z", (void*)nativeIsValid},
+ {"nativeIsConsumerRunningBehind", "(J)Z", (void*)nativeIsConsumerRunningBehind},
+ {"nativeLockCanvas", "(JLandroid/graphics/Canvas;Landroid/graphics/Rect;)J",
+ (void*)nativeLockCanvas},
+ {"nativeUnlockCanvasAndPost", "(JLandroid/graphics/Canvas;)V",
+ (void*)nativeUnlockCanvasAndPost},
+ {"nativeAllocateBuffers", "(J)V", (void*)nativeAllocateBuffers},
+ {"nativeCreateFromSurfaceControl", "(J)J", (void*)nativeCreateFromSurfaceControl},
+ {"nativeGetFromSurfaceControl", "(JJ)J", (void*)nativeGetFromSurfaceControl},
+ {"nativeReadFromParcel", "(JLandroid/os/Parcel;)J", (void*)nativeReadFromParcel},
+ {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel},
+ {"nativeGetWidth", "(J)I", (void*)nativeGetWidth},
+ {"nativeGetHeight", "(J)I", (void*)nativeGetHeight},
+ {"nativeGetNextFrameNumber", "(J)J", (void*)nativeGetNextFrameNumber},
+ {"nativeSetScalingMode", "(JI)I", (void*)nativeSetScalingMode},
+ {"nativeForceScopedDisconnect", "(J)I", (void*)nativeForceScopedDisconnect},
+ {"nativeAttachAndQueueBufferWithColorSpace", "(JLandroid/hardware/HardwareBuffer;I)I",
+ (void*)nativeAttachAndQueueBufferWithColorSpace},
+ {"nativeSetSharedBufferModeEnabled", "(JZ)I", (void*)nativeSetSharedBufferModeEnabled},
+ {"nativeSetAutoRefreshEnabled", "(JZ)I", (void*)nativeSetAutoRefreshEnabled},
+ {"nativeSetFrameRate", "(JFI)I", (void*)nativeSetFrameRate},
+ {"nativeGetFromBlastBufferQueue", "(JJ)J", (void*)nativeGetFromBlastBufferQueue},
};
int register_android_view_Surface(JNIEnv* env)
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 5f590be..e1a980c 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -120,6 +120,14 @@
}
optional Assist assist = 7;
+ message AssistHandles {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto learning_time_elapsed_millis = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto learning_event_count = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional AssistHandles assist_handles = 86;
+
message Autofill {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -617,5 +625,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 86;
+ // Next tag = 87;
}
diff --git a/core/proto/android/stats/style/style_enums.proto b/core/proto/android/stats/style/style_enums.proto
index f3f491f..828e412 100644
--- a/core/proto/android/stats/style/style_enums.proto
+++ b/core/proto/android/stats/style/style_enums.proto
@@ -38,6 +38,9 @@
LIVE_WALLPAPER_APPLIED = 16;
LIVE_WALLPAPER_INFO_SELECT = 17;
LIVE_WALLPAPER_CUSTOMIZE_SELECT = 18;
+ LIVE_WALLPAPER_QUESTIONNAIRE_SELECT = 19;
+ LIVE_WALLPAPER_QUESTIONNAIRE_APPLIED = 20;
+ LIVE_WALLPAPER_EFFECT_SHOW = 21;
}
enum LocationPreference {
@@ -46,3 +49,9 @@
LOCATION_CURRENT = 2;
LOCATION_MANUAL = 3;
}
+
+enum DatePreference {
+ DATE_PREFERENCE_UNSPECIFIED = 0;
+ DATE_UNAVAILABLE = 1;
+ DATE_MANUAL = 2;
+}
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index f5facea..b54dfc0 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d uur lank</item>
<item quantity="one">1 uur lank</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (volgende wekker)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Totdat jy dit afskakel"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index bea1861..668ce42 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">ለ%d ሰዓት</item>
<item quantity="other">ለ%d ሰዓት</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ድረስ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"እስከ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ቀጣይ ማንቂያ)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"እስኪያጠፉት ድረስ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 6832344..fdb351c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1953,6 +1953,7 @@
<item quantity="other">لمدة %d من الساعات</item>
<item quantity="one">لمدة ساعة</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"حتى <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (التنبيه التالي)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"إلى أن يتم إيقاف الوضع"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index bad330e..93ce15f 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">%d ঘণ্টাৰ বাবে</item>
<item quantity="other">%d ঘণ্টাৰ বাবে</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পৰ্যন্ত"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পৰ্যন্ত"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (পৰৱৰ্তী এলার্ম) পর্যন্ত"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"আপুনি অফ নকৰা পর্যন্ত"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index c721bbf..01b815b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d saat üçün</item>
<item quantity="one">1 saat üçün</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Bu vaxtadək: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Saat <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> qədər"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> radəsinə qədər (növbəti siqnal)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Deaktiv edənə qədər"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 93ddbe5..e2fc8f1 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -243,7 +243,7 @@
<string name="global_action_power_off" msgid="4404936470711393203">"Isključi"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"Napajanje"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Restartuj"</string>
- <string name="global_action_emergency" msgid="1387617624177105088">"Hitni poziv"</string>
+ <string name="global_action_emergency" msgid="1387617624177105088">"Hitan poziv"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"Izveštaj o grešci"</string>
<string name="global_action_logout" msgid="6093581310002476511">"Završi sesiju"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Snimak ekrana"</string>
@@ -835,7 +835,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pritisnite „Meni“ da biste otključali telefon ili uputite hitan poziv."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Pritisnite „Meni“ za otključavanje."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Unesite šablon za otključavanje"</string>
- <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Hitni poziv"</string>
+ <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Hitan poziv"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Nazad na poziv"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Tačno!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Probajte ponovo"</string>
@@ -1815,7 +1815,7 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ažurirao je administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Izbrisao je administrator"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Potvrdi"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“\n\n"<annotation id="url">"Saznajte više"</annotation></string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer „Ok Google“\n\n"<annotation id="url">"Saznajte više"</annotation></string>
<string name="battery_saver_description" msgid="6794188153647295212">"Da bi se produžilo trajanje baterije, Ušteda baterije:\n\n• uključuje tamnu temu\n• isključuje ili ograničava aktivnosti u pozadini, neke vizuelne efekte i druge funkcije, na primer, „Ok Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Da bi se smanjila potrošnja podataka, Ušteda podataka sprečava neke aplikacije da šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može da pristupa podacima, ali će to činiti ređe. Na primer, slike se neće prikazivati dok ih ne dodirnete."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Želite da uključite Uštedu podataka?"</string>
@@ -1860,6 +1860,7 @@
<item quantity="few">Za %d s</item>
<item quantity="other">Za %d s</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sledeći alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 08aeacf..45008f2 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="many">На %d гадз</item>
<item quantity="other">На %d гадз</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Да <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Да <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Да <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (наступны будзільнік)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Пакуль не выключыце"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index e49ae26..0c2c022 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">За %d ч</item>
<item quantity="one">За 1 ч</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До следващия будилник (<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"До изключване"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 22589cd..b10d8dc 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="one">%d ঘন্টার জন্য</item>
<item quantity="other">%d ঘন্টার জন্য</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> পর্যন্ত (পরবর্তী অ্যালার্ম)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"যতক্ষণ না আপনি বন্ধ করছেন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 755ee47..17c0d1d 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -593,7 +593,7 @@
<string name="face_acquired_not_detected" msgid="2945945257956443257">"Postavite lice direktno ispred telefona"</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Previše pokreta. Držite telefon mirno."</string>
<string name="face_acquired_recalibrate" msgid="8724013080976469746">"Ponovo registrirajte lice."</string>
- <string name="face_acquired_too_different" msgid="4699657338753282542">"Nije više moguće prepoznati lice. Pokušajte opet."</string>
+ <string name="face_acquired_too_different" msgid="4699657338753282542">"Više nije moguće prepoznati lice. Pokušajte opet."</string>
<string name="face_acquired_too_similar" msgid="7684650785108399370">"Previše slično, promijenite položaj."</string>
<string name="face_acquired_pan_too_extreme" msgid="7822191262299152527">"Malo manje zakrenite glavu."</string>
<string name="face_acquired_tilt_too_extreme" msgid="8119978324129248059">"Malo manje zakrenite glavu."</string>
@@ -1860,6 +1860,7 @@
<item quantity="few">%d sata</item>
<item quantity="other">%d sati</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 4f55bf5..e0c709d 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -1224,7 +1224,7 @@
<string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"S\'ha establert el so de silenci"</string>
<string name="volume_call" msgid="7625321655265747433">"Volum en trucada"</string>
<string name="volume_bluetooth_call" msgid="2930204618610115061">"Volum en trucada per Bluetooth"</string>
- <string name="volume_alarm" msgid="4486241060751798448">"Volum de l\'alarma"</string>
+ <string name="volume_alarm" msgid="4486241060751798448">"Volum d\'alarma"</string>
<string name="volume_notification" msgid="6864412249031660057">"Volum de notificacions"</string>
<string name="volume_unknown" msgid="4041914008166576293">"Volum"</string>
<string name="volume_icon_description_bluetooth" msgid="7540388479345558400">"Volum del Bluetooth"</string>
@@ -1829,6 +1829,7 @@
<item quantity="other">Durant %d h</item>
<item quantity="one">Durant 1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Finalitza: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Fins a les <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (propera alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Fins que no el desactivis"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index fcf9b83..095ec69 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="other">%d h</item>
<item quantity="one">1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (příští budík)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dokud tuto funkci nevypnete"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0b7e396..499b9de 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">I %d t.</item>
<item quantity="other">I %d t.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Indtil <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (næste alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Indtil du deaktiverer"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index faa78df..867efac 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="other">Für %d h</item>
<item quantity="one">Für 1 h</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nächste Weckzeit)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Bis zur Deaktivierung"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 2e1b56a..d4d1c5a 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Για %d ώρες</item>
<item quantity="one">Για 1 ώρα</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Έως <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Έως τις <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Μέχρι τις <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (επόμενο ξυπνητήρι)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Μέχρι την απενεργοποίηση"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index dcfbf5a..f140ce3 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 65f2426..e814bdf 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index d38e2fe..73f1562 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 30a9bf88..122ea7f 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 6503f7c..53d8c66 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">For %d hr</item>
<item quantity="one">For 1 hr</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Until <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (next alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Until you turn off"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 2308b5a..953b6fc 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Durante %d horas</item>
<item quantity="one">Durante 1 hora</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hasta la(s) <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hasta la hora <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Hasta que lo desactives"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 67350cb0..465d2ec 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Durante %d horas</item>
<item quantity="one">Durante 1 hora</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hasta <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hasta las <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Hasta que lo desactives"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 3929ed4..6d2a4c0f 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d h</item>
<item quantity="one">1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Kuni <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (järgmine äratus)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kuni välja lülitate"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index c93dbbf..7241b97 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d orduz</item>
<item quantity="one">Ordubetez</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> arte (hurrengo alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Zuk desaktibatu arte"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 1ee4be5..02f4728 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1267,7 +1267,7 @@
<string name="sms_control_title" msgid="4748684259903148341">"درحال ارسال پیامکها"</string>
<string name="sms_control_message" msgid="6574313876316388239">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> درحال ارسال تعداد زیادی پیامک است. آیا اجازه میدهید این برنامه همچنان پیامک ارسال کند؟"</string>
<string name="sms_control_yes" msgid="4858845109269524622">"مجاز است"</string>
- <string name="sms_control_no" msgid="4845717880040355570">"اجازه ندارد"</string>
+ <string name="sms_control_no" msgid="4845717880040355570">"مجاز نبودن"</string>
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> مایل است پیامی به <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b> ارسال کند."</string>
<string name="sms_short_code_details" msgid="2723725738333388351">"این مورد "<b>"شاید هزینهای"</b>" را به حساب دستگاه همراهتان بگذارد."</string>
<string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"این مورد هزینهای را به حساب دستگاه همراهتان میگذارد."</b></string>
@@ -1413,7 +1413,7 @@
<string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"میخواهید به این درخواست اجازه دهید؟"</string>
<string name="grant_permissions_header_text" msgid="3420736827804657201">"درخواست دسترسی"</string>
<string name="allow" msgid="6195617008611933762">"ارزیابیشده"</string>
- <string name="deny" msgid="6632259981847676572">"اجازه ندارد"</string>
+ <string name="deny" msgid="6632259981847676572">"مجاز نبودن"</string>
<string name="permission_request_notification_title" msgid="1810025922441048273">"مجوز درخواست شد"</string>
<string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"مجوز\nبرای حساب <xliff:g id="ACCOUNT">%s</xliff:g> درخواست شد."</string>
<string name="forward_intent_to_owner" msgid="4620359037192871015">"شما از این برنامه در خارج از نمایه کاریتان استفاده میکنید"</string>
@@ -1639,7 +1639,7 @@
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"مشاهده و انجام کنشها"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"این عملکرد میتواند با برنامه یا حسگری سختافزاری تعاملاتتان را ردیابی کند و ازطرف شما با برنامهها تعامل داشته باشد."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"مجاز"</string>
- <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"رد کردن"</string>
+ <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"مجاز نبودن"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"برای استفاده از ویژگی، روی آن ضربه بزنید:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"انتخاب ویژگیهای موردنظر برای استفاده با دکمه دسترسپذیری"</string>
<string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"انتخاب ویژگیهای موردنظر برای استفاده با میانبر کلید میزان صدا"</string>
@@ -1829,6 +1829,7 @@
<item quantity="one">برای %d ساعت</item>
<item quantity="other">برای %d ساعت</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"تا <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (زنگ بعدی)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"تا زمانیکه آن را خاموش کنید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 1eff983..0c94f35 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d tunnin ajan</item>
<item quantity="one">1 tunnin ajan</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> asti"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Kunnes kello on <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> asti (seuraava hälytys)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kunnes laitat pois päältä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index c6f347b..dcfe8ad 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Pendant %d h</item>
<item quantity="other">Pendant %d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Jusqu\'à la désactivation"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 6307c35..5fc2d69 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Pendant %d h</item>
<item quantity="other">Pendant %d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Jusqu\'à <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarme suivante)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Jusqu\'à la désactivation"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 0f28146..a6eac55 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Durante %d h</item>
<item quantity="one">Durante unha h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Ata: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Ata as <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Ata as <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próxima alarma)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Ata a desactivación"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index f11a228..6b4bbf3 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="one">%d કલાક માટે</item>
<item quantity="other">%d કલાક માટે</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> સુધી"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (આગલા એલાર્મ) સુધી"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"તમે બંધ ન કરો ત્યાં સુધી"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 7b0bd23..3bf9dcd 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">%d घंटे के लिए</item>
<item quantity="other">%d घंटे के लिए</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> तक"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> तक"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अगले अलार्म) तक"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"जब तक आप बंद नहीं करते"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index eb41c9f..d350d04 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1860,6 +1860,7 @@
<item quantity="few">%d h</item>
<item quantity="other">%d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sljedeći alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dok ne isključite"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 3b44295..70f87b6 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d órán keresztül</item>
<item quantity="one">1 órán keresztül</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Eddig: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ez a következő ébresztés)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kikapcsolásig"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 4f66b6e..32ac6a8 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -48,7 +48,7 @@
<string name="invalidPin" msgid="7542498253319440408">"Մուտքագրեք PIN, որը 4-ից 8 թիվ է:"</string>
<string name="invalidPuk" msgid="8831151490931907083">"Մուտքագրեք PUK, որն 8 կամ ավել թիվ ունի:"</string>
<string name="needPuk" msgid="7321876090152422918">"Ձեր SIM քարտը PUK-ով կողպված է: Մուտքագրեք PUK կոդը այն ապակողպելու համար:"</string>
- <string name="needPuk2" msgid="7032612093451537186">"Մուտքագրեք PUK2-ը` SIM քարտն արգելահանելու համար:"</string>
+ <string name="needPuk2" msgid="7032612093451537186">"Մուտքագրեք PUK2-ը՝ SIM քարտն արգելահանելու համար:"</string>
<string name="enablePin" msgid="2543771964137091212">"Ձախողվեց: Միացրեք SIM/RUIM կողպումը:"</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="one">Մնաց <xliff:g id="NUMBER_1">%d</xliff:g> փորձ, որից հետո SIM քարտն արգելափակվելու է:</item>
@@ -411,7 +411,7 @@
<string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Թույլ է տալիս հավելվածին փոփոխել ձեր պլանշետի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Թույլ է տալիս հավելվածին փոփոխել Android TV սարքի զանգերի մատյանը, այդ թվում՝ մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել՝ ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Թույլ է տալիս հավելվածին փոփոխել ձեր հեռախոսի զանգերի մատյանը, այդ թվում` մուտքային և ելքային զանգերի մասին տվյալները: Վնասարար հավելվածները կարող են սա օգտագործել` ձեր զանգերի մատյանը ջնջելու կամ փոփոխելու համար:"</string>
- <string name="permlab_bodySensors" msgid="3411035315357380862">"օգտագործել մարմնի սենսորները (օրինակ` սրտի կծկումների հաճախականության չափիչ)"</string>
+ <string name="permlab_bodySensors" msgid="3411035315357380862">"օգտագործել մարմնի սենսորները (օրինակ՝ սրտի կծկումների հաճախականության չափիչ)"</string>
<string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Հավելվածին թույլ է տալիս մուտք ունենալ սենսորների տվյալներին, որոնք վերահսկում են ձեր ֆիզիկական վիճակը, օրինակ՝ ձեր սրտի զարկերը:"</string>
<string name="permlab_readCalendar" msgid="6408654259475396200">"Կարդալ օրացույցի միջոցառումները և տվյալները"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"Այս հավելվածը կարող է կարդալ օրացույցի՝ ձեր պլանշետում պահված բոլոր միջոցառումները, ինչպես նաև հրապարակել կամ պահել ձեր օրացույցի տվյալները:"</string>
@@ -430,7 +430,7 @@
<string name="permlab_accessBackgroundLocation" msgid="1721164702777366138">"տեղադրության մասին տվյալների հասանելիություն ֆոնային ռեժիմում"</string>
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Այս հավելվածը ցանկացած ժամանակ կարող է տեսնել տեղադրության տվյալները, նույնիսկ երբ այն ակտիվ չէ։"</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"փոխել ձեր աուդիո կարգավորումները"</string>
- <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ` ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string>
+ <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Թույլ է տալիս հավելվածին փոփոխել ձայնանյութի գլոբալ կարգավորումները, ինչպես օրինակ՝ ձայնը և թե որ խոսափողն է օգտագործված արտածման համար:"</string>
<string name="permlab_recordAudio" msgid="1208457423054219147">"ձայնագրել աուդիո ֆայլ"</string>
<string name="permdesc_recordAudio" msgid="3976213377904701093">"Այս հավելվածը ցանկացած պահի կարող է ձայնագրել խոսափողի օգնությամբ:"</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"ուղարկել հրամաններ SIM քարտին"</string>
@@ -495,7 +495,7 @@
<string name="permlab_changeTetherState" msgid="9079611809931863861">"փոխել միացված կապը"</string>
<string name="permdesc_changeTetherState" msgid="3025129606422533085">"Թույլ է տալիս հավելվածին փոխել կապված ցանցի միացման կարգավիճակը:"</string>
<string name="permlab_accessWifiState" msgid="5552488500317911052">"դիտել Wi-Fi կապերը"</string>
- <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Թույլ է տալիս հավելվածին տեսնել Wi-Fi ցանցի տեղեկություններ, ինչպես օրինակ` արդյոք Wi-Fi-ը միացված է, թե` ոչ, և միացված Wi-Fi սարքի անունը:"</string>
+ <string name="permdesc_accessWifiState" msgid="6913641669259483363">"Թույլ է տալիս հավելվածին տեսնել Wi-Fi ցանցի տեղեկություններ, ինչպես օրինակ՝ արդյոք Wi-Fi-ը միացված է, թե` ոչ, և միացված Wi-Fi սարքի անունը:"</string>
<string name="permlab_changeWifiState" msgid="7947824109713181554">"միանալ Wi-Fi-ին և անջատվել դրանից"</string>
<string name="permdesc_changeWifiState" msgid="7170350070554505384">"Թույլ է տալիս հավելվածին միանալ Wi-Fi մուտքի կետերին և անջատվել այդ կետերից, ինչպես նաև կատարել սարքի կարգավորման փոփոխություններ Wi-Fi ցանցերի համար:"</string>
<string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"թույլատրել Բազմասփյուռ Wi-Fi-ի ընդունումը"</string>
@@ -615,7 +615,7 @@
</string-array>
<string name="face_icon_content_description" msgid="465030547475916280">"Դեմքի պատկերակ"</string>
<string name="permlab_readSyncSettings" msgid="6250532864893156277">"կարդալ համաժամացման կարգավորումները"</string>
- <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Թույլ է տալիս հավելվածին կարդալ համաժամացման կարգավորումները հաշվի համար: Օրինակ` այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամացված է հաշվի հետ:"</string>
+ <string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Թույլ է տալիս հավելվածին կարդալ համաժամացման կարգավորումները հաշվի համար: Օրինակ՝ այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամացված է հաշվի հետ:"</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"համաժամացումը փոխարկել միացվածի և անջատվածի"</string>
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Թույլ է տալիս հավելվածին փոփոխել համաժամացման կարգավորումները հաշվի համար: Օրինակ, այն կարող է օգտագործվել` միացնելու Մարդիկ հավելվածի համաժամացումը հաշվի հետ:"</string>
<string name="permlab_readSyncStats" msgid="3747407238320105332">"կարդալ համաժամացման վիճակագրությունը"</string>
@@ -876,7 +876,7 @@
<string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Մոռացե՞լ եք սխեման:"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Հաշվի ապակողպում"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Չափից շատ սխեմայի փորձեր"</string>
- <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ապակողպելու համար` մուտք գործեք ձեր Google հաշվով:"</string>
+ <string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Ապակողպելու համար՝ մուտք գործեք ձեր Google հաշվով:"</string>
<string name="lockscreen_glogin_username_hint" msgid="6916101478673157045">"Օգտանուն (էլփոստ)"</string>
<string name="lockscreen_glogin_password_hint" msgid="3031027901286812848">"Գաղտնաբառ"</string>
<string name="lockscreen_glogin_submit_button" msgid="3590556636347843733">"Մուտք գործել"</string>
@@ -1407,7 +1407,7 @@
<string name="ime_action_done" msgid="6299921014822891569">"Պատրաստ է"</string>
<string name="ime_action_previous" msgid="6548799326860401611">"Նախորդ"</string>
<string name="ime_action_default" msgid="8265027027659800121">"Կատարել"</string>
- <string name="dial_number_using" msgid="6060769078933953531">"Հավաքել հեռախոսահամարը`\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string>
+ <string name="dial_number_using" msgid="6060769078933953531">"Հավաքել հեռախոսահամարը՝\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string>
<string name="create_contact_using" msgid="6200708808003692594">"Ստեղծել կոնտակտ`\nօգտագործելով <xliff:g id="NUMBER">%s</xliff:g>-ը"</string>
<string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Հետևյալ մեկ կամ մի քանի հավելվածներին թույլտվություն է անհրաժեշտ՝ այժմ և հետագայում ձեր հաշվի տվյալներն օգտագործելու համար։"</string>
<string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Թույլատրե՞լ"</string>
@@ -1474,7 +1474,7 @@
<string name="number_picker_increment_button" msgid="7621013714795186298">"Ավելացնել"</string>
<string name="number_picker_decrement_button" msgid="5116948444762708204">"Նվազեցնել"</string>
<string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> հպեք և պահեք:"</string>
- <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Սահեցրեք վերև` ավելացնելու համար, և ներքև` նվազեցնելու համար:"</string>
+ <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Սահեցրեք վերև՝ ավելացնելու համար, և ներքև՝ նվազեցնելու համար:"</string>
<string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Աճեցնել րոպեն"</string>
<string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Նվազեցնել րոպեն"</string>
<string name="time_picker_increment_hour_button" msgid="3063572723197178242">"Աճեցնել ժամը"</string>
@@ -1598,7 +1598,7 @@
<string name="kg_invalid_puk" msgid="4809502818518963344">"Վերամուտքագրեք ճիշտ PUK ծածկագիրը: Կրկնվող փորձերը ընդմիշտ կկասեցնեն SIM քարտը:"</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"PIN ծածկագրերը չեն համընկնում"</string>
<string name="kg_login_too_many_attempts" msgid="699292728290654121">"Չափից շատ սխեմայի փորձեր"</string>
- <string name="kg_login_instructions" msgid="3619844310339066827">"Ապակողպելու համար` մուտք գործեք ձեր Google հաշվով:"</string>
+ <string name="kg_login_instructions" msgid="3619844310339066827">"Ապակողպելու համար՝ մուտք գործեք ձեր Google հաշվով:"</string>
<string name="kg_login_username_hint" msgid="1765453775467133251">"Օգտանուն (էլփոստ)"</string>
<string name="kg_login_password_hint" msgid="3330530727273164402">"Գաղտնաբառը"</string>
<string name="kg_login_submit_button" msgid="893611277617096870">"Մուտք գործել"</string>
@@ -1829,6 +1829,7 @@
<item quantity="one">%d ժամով</item>
<item quantity="other">%d ժամով</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Մինչև <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Մինչև <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Մինչև ժ. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-ը (հաջորդ զարթուցիչը)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Մինչև չանջատեք"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f6f5040..f8ba016 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Selama %d jam</item>
<item quantity="one">Selama 1 jam</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Sampai <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarm berikutnya)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Sampai Anda menonaktifkannya"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 899fa52..9278bbb 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Í %d klst.</item>
<item quantity="other">Í %d klst.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Þangað til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (næsta viðvörun)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Þar til þú slekkur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 14bfa03..b962d8b 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Per %d ore</item>
<item quantity="one">Per 1 ora</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Fino a: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Fino a <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (prossima sveglia)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Fino alla disattivazione"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 2b6d226..821e4fd 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="other">למשך %d שעות</item>
<item quantity="one">למשך שעה אחת</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"עד <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ההתראה הבאה)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"עד הכיבוי"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index fdb505b..4b48495 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d時間</item>
<item quantity="one">1時間</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>まで"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>まで"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(次のアラーム)まで"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"自分が OFF にするまで"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 3d0d88b..cdac647 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d სთ.</item>
<item quantity="one">1 სთ.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>-მდე (შემდეგი მაღვიძარა)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"გამორთვამდე"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 9798cc9..608ed1e 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d сағат</item>
<item quantity="one">1 сағат</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> дейін (келесі дабыл)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Өшірілгенге дейін"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index a0b8faa..14b0189 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">អស់រយៈពេល %d ម៉ោង</item>
<item quantity="one">អស់រយៈពេល 1 ម៉ោង</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"រហូតដល់ម៉ោង <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"រហូតដល់ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"រហូតដល់ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ម៉ោងរោទិ៍បន្ទាប់)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"រហូតទាល់តែអ្នកបិទ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index c7786ae..0fe1c24 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="one">%d ಗಂಟೆಗೆ</item>
<item quantity="other">%d ಗಂಟೆಗೆ</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ವರೆಗೆ (ಮುಂದಿನ ಅಲಾರಮ್)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ನೀವು ಆಫ್ ಮಾಡುವವರೆಗೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 78c3286..e3406c1b 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d시간 동안</item>
<item quantity="one">1시간 동안</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>까지"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>까지"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(다음 알람)까지"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"사용 중지할 때까지"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 162b4d3..3268d16 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d саатка</item>
<item quantity="one">1 саатка</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> чейин (кийинки ойготкуч)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Бул функция өчүрүлгөнгө чейин"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 4451139..d22ce0c 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">ເປັນເວລາ %d ຊມ</item>
<item quantity="one">ເປັນເວລາ 1 ຊມ</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"ຈົນຮອດ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"ຈົນຮອດ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"ຈົນກ່ວາ <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ສັນຍານເຕືອນຕໍ່ໄປ)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ຈົນກວ່າທ່ານຈະປິດ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index fa07978..ff687c5 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="many">%d val.</item>
<item quantity="other">%d val.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Iki <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (kitas signalas)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Kol išjungsite"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index e8107fb..2cb6058 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1860,6 +1860,7 @@
<item quantity="one">%d h</item>
<item quantity="other">%d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Līdz: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Līdz <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Līdz plkst. <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nākamais signāls)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Līdz brīdim, kad izslēgsiet"</string>
diff --git a/core/res/res/values-mcc310-mnc150-as/strings.xml b/core/res/res/values-mcc310-mnc150-as/strings.xml
deleted file mode 100644
index 2a4e46bd6..0000000
--- a/core/res/res/values-mcc310-mnc150-as/strings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/* //device/apps/common/assets/res/any/strings.xml
-**
-** Copyright 2006, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_illegal_me" msgid="8004509200390992737">"ফ\'নৰ অনুমতি নাই MM#6"</string>
-</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index df7781e..bf7220c 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">За %d ч.</item>
<item quantity="other">За %d ч.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следниот аларм)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Додека не го исклучите"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 12c0cb4..513ed1d 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d മണിക്കൂറത്തേക്ക്</item>
<item quantity="one">ഒരു മണിക്കൂറത്തേക്ക്</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> വരെ (അടുത്ത അലാറം)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"നിങ്ങൾ ഓഫാക്കുന്നത് വരെ"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 2c041dc..b95b689 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d цагийн турш</item>
<item quantity="one">1 цагийн турш:</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> хүртэл (дараагийн сэрүүлэг)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Таныг унтраах хүртэл"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index da6f2b7..40c7dbd 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="other">%d तासासाठी</item>
<item quantity="one">1 तासासाठी</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> पर्यंत (पुढील अलार्म)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"तुम्ही बंद करेपर्यंत"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 2e78b45..fb24a06 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Selama %d jam</item>
<item quantity="one">Selama 1 jam</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Sehingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Sehingga <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (penggera akan datang)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Sehingga anda matikan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 1ebafa4..2c3fbcb 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d နာရီ အတွက်</item>
<item quantity="one">၁ နာရီအတွက်</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> အထိ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>အထိ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> အထိ (လာမည့် နှိုးစက်)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"သင်ပိတ်လိုက်သည် အထိ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ac21aec..fced207 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">I %d timer</item>
<item quantity="one">I én time</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Til <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (neste alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Til du slår av"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index ee0362e..fadabe8 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="other">%d घन्टाका लागि</item>
<item quantity="one">१ घन्टाको लागि</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> सम्म"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (अर्को अलार्म) सम्म"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"तपाईंले निष्क्रिय नपार्नुभएसम्म"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 14cfee0..337bf17 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Gedurende %d u</item>
<item quantity="one">Gedurende 1 u</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Tot <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (volgende wekker)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Totdat je uitschakelt"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 1cb01ea..85ca065 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d ଘଣ୍ଟା ପାଇଁ</item>
<item quantity="one">1 ଘଣ୍ଟା ପାଇଁ</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ପରବର୍ତ୍ତୀ ଆଲାର୍ମ) ପର୍ଯ୍ୟନ୍ତ"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ବନ୍ଦ ନକରିବା ପର୍ଯ୍ୟନ୍ତ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index b5560f1..ed663a8 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="one">%d ਘੰਟਿਆਂ ਲਈ</item>
<item quantity="other">%d ਘੰਟਿਆਂ ਲਈ</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> ਤੱਕ (ਅਗਲਾ ਅਲਾਰਮ)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index aeebf88..16b3faf 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="other">%d godz.</item>
<item quantity="one">1 godz.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (następny alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dopóki nie wyłączysz"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index aedc047..2883d6f 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Por %d horas</item>
<item quantity="other">Por %d horas</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Até você desativar"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 883e4bf..3bc9530 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Durante %d h</item>
<item quantity="one">Durante 1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Até desativar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index aedc047..2883d6f 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Por %d horas</item>
<item quantity="other">Por %d horas</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Até você desativar"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index ebc5827..d5a9297 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1860,6 +1860,7 @@
<item quantity="other">Pentru %d h</item>
<item quantity="one">Pentru 1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Până <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Până la <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (următoarea alarmă)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Până când dezactivați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index f3d13ce..3339fb5 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="many">На %d часов</item>
<item quantity="other">На %d часа</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (будильник)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Пока вы не отключите"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index cc6783a..c26f23f 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">පැය %d ක් සඳහා</item>
<item quantity="other">පැය %d ක් සඳහා</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> දක්වා"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> තෙක්"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> තෙක් (ඊළඟ එලාමය)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ඔබ ක්රියාවිරහිත කරන තුරු"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b612565..1a4f07e 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="other">Na %d h</item>
<item quantity="one">Na 1 h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (ďalší budík)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dokým funkciu nevypnete"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 3ef51a7..0079e38 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="few">%d h</item>
<item quantity="other">%d h</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Do <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (naslednji alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Dokler ne izklopite"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 37a995a5..b29582c 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Për %d orë</item>
<item quantity="one">Për 1 orë</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Deri në <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (alarmi tjetër)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Derisa ta çaktivizosh"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index a3eb3e4..f24e285 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -243,7 +243,7 @@
<string name="global_action_power_off" msgid="4404936470711393203">"Искључи"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"Напајање"</string>
<string name="global_action_restart" msgid="4678451019561687074">"Рестартуј"</string>
- <string name="global_action_emergency" msgid="1387617624177105088">"Хитни позив"</string>
+ <string name="global_action_emergency" msgid="1387617624177105088">"Хитан позив"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"Извештај о грешци"</string>
<string name="global_action_logout" msgid="6093581310002476511">"Заврши сесију"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Снимак екрана"</string>
@@ -835,7 +835,7 @@
<string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Притисните „Мени“ да бисте откључали телефон или упутите хитан позив."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Притисните „Мени“ за откључавање."</string>
<string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Унесите шаблон за откључавање"</string>
- <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Хитни позив"</string>
+ <string name="lockscreen_emergency_call" msgid="7549683825868928636">"Хитан позив"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Назад на позив"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Тачно!"</string>
<string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Пробајте поново"</string>
@@ -1815,7 +1815,7 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Ажурирао је администратор"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Избрисао је администратор"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"Потврди"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“\n\n"<annotation id="url">"Сазнајте више"</annotation></string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример „Ок Google“\n\n"<annotation id="url">"Сазнајте више"</annotation></string>
<string name="battery_saver_description" msgid="6794188153647295212">"Да би се продужило трајање батерије, Уштеда батерије:\n\n• укључује тамну тему\n• искључује или ограничава активности у позадини, неке визуелне ефекте и друге функције, на пример, „Ок Google“"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Да би се смањила потрошња података, Уштеда података спречава неке апликације да шаљу или примају податке у позадини. Апликација коју тренутно користите може да приступа подацима, али ће то чинити ређе. На пример, слике се неће приказивати док их не додирнете."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Желите да укључите Уштеду података?"</string>
@@ -1860,6 +1860,7 @@
<item quantity="few">За %d с</item>
<item quantity="other">За %d с</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (следећи аларм)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Док не искључите"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 3711e2b..5c9ce2c 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">I %d tim</item>
<item quantity="one">I en 1 tim</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Till <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (nästa alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Tills du stänger av"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 23b6c3b..55db3eb 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Kwa saa %d </item>
<item quantity="one">Kwa saa 1 </item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hadi <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hadi <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Mpaka <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (kengele inayofuata)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Hadi utakapoizima"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 42b2d9c..be030d1 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d மணிநேரத்திற்கு</item>
<item quantity="one">1 மணிநேரத்திற்கு</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> வரை"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> வரை"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> மணி (அடுத்த அலாரம்) வரை"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"ஆஃப் செய்யும் வரை"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 6b9caf1..0966d23 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d గం పాటు</item>
<item quantity="one">1 గం పాటు</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> వరకు"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> వరకు"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (తర్వాత అలారం) వరకు"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"మీరు ఆఫ్ చేసే వరకు"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 4b39c63..7ff76ec 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">เป็นเวลา %d ชม.</item>
<item quantity="one">เป็นเวลา 1 ชม.</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"จนถึง <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"จนถึงเวลา <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"จนถึงเวลา <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (การปลุกครั้งถัดไป)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"จนกว่าคุณจะปิด"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index beebbd2..7d9c717 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Sa loob ng %d oras</item>
<item quantity="other">Sa loob ng %d na oras</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Hanggang <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (susunod na alarm)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Hanggang sa i-off mo"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 17852a6..a658bf1 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d saat</item>
<item quantity="one">1 saat</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Şu saate kadar: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Şu saate kadar: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (sonraki alarma) saatine kadar"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Siz kapatana kadar"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 31684e0..7e81784 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1891,6 +1891,7 @@
<item quantity="many">Протягом %d год</item>
<item quantity="other">Протягом %d год</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"До: <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"До <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (наступний будильник)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Доки ви не вимкнете"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index ed32b24..b9b9c58 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1829,6 +1829,8 @@
<item quantity="other">%d گھنٹے کیلئے</item>
<item quantity="one">1 گھنٹہ کیلئے</item>
</plurals>
+ <!-- no translation found for zen_mode_until_next_day (1403042784161725038) -->
+ <skip />
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> تک (اگلا الارم)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"یہاں تک کہ آپ آف کر دیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 7671809..5edd9dc 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d soat</item>
<item quantity="one">1 soat</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> gacha (keyingi signal)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Rejimdan chiqilgunicha"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 1899f8f..e3c7658 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">Trong %d giờ</item>
<item quantity="one">Trong 1 giờ</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Cho tới <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Cho đến <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Cho tới <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (cảnh báo tiếp theo)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Cho đến khi bạn tắt"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 0a241da..dee9d95 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d 小时</item>
<item quantity="one">1 小时</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"结束时间:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"直到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>(闹钟下次响铃时)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"直到您将其关闭"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 6e584d6..44ad863 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d 小時</item>
<item quantity="one">1 小時</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"完成時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"直至<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (下一次響鬧)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"直至您關閉為止"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index ab1a423..8831bf0 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="other">%d 小時</item>
<item quantity="one">1 小時</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"結束時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"結束時間:<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"到<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> 為止 (下一個鬧鐘)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"直到你關閉為止"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 582d435..5fb9fa6 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1829,6 +1829,7 @@
<item quantity="one">Ngamahora angu-%d</item>
<item quantity="other">Ngamahora angu-%d</item>
</plurals>
+ <string name="zen_mode_until_next_day" msgid="1403042784161725038">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_until" msgid="2250286190237669079">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<string name="zen_mode_alarm" msgid="7046911727540499275">"Kuze kube ngu-<xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (i-alamu elandelayo)"</string>
<string name="zen_mode_forever" msgid="740585666364912448">"Uze uvale isikrini"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 03975da..25a9bbd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4374,7 +4374,7 @@
</string>
<!-- Dialog title for dialog shown when the multiple accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] -->
- <string name="accessibility_shortcut_multiple_service_warning_title">Turn on accessibility features?</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title">Turn on shortcut for accessibility features?</string>
<!-- Message shown in dialog when user is in the process of enabling the multiple accessibility service via the volume buttons shortcut for the first time. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_multiple_service_warning">Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="service" example="TalkBack">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility.</string>
@@ -4383,7 +4383,7 @@
<string name="accessibility_shortcut_multiple_service_list">\t• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string>
<!-- Dialog title for dialog shown when this accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] -->
- <string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g>?</string>
+ <string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g> shortcut?</string>
<!-- Message shown in dialog when user is in the process of enabling this accessibility service via the volume buttons shortcut for the first time. [CHAR LIMIT=none] -->
<string name="accessibility_shortcut_single_service_warning">Holding down both volume keys for a few seconds turns on <xliff:g id="service" example="TalkBack">%1$s</xliff:g>, an accessibility feature. This may change how your device works.\n\nYou can change this shortcut to another feature in Settings > Accessibility.</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6ae6faa..71cb2ac 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -228,15 +228,6 @@
</intent-filter>
</activity>
- <activity android:name="android.widget.focus.ListOfButtons"
- android:label="ListOfButtons"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
- </intent-filter>
- </activity>
-
<activity android:name="android.widget.focus.LinearLayoutGrid"
android:label="LinearLayoutGrid"
android:exported="true">
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 2bf9848..7766b57 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -192,7 +192,7 @@
PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putInt("k", 4);
FixedRotationAdjustments fixedRotationAdjustments = new FixedRotationAdjustments(
- Surface.ROTATION_90, DisplayCutout.NO_CUTOUT);
+ Surface.ROTATION_90, 1920, 1080, DisplayCutout.NO_CUTOUT);
LaunchActivityItem item = LaunchActivityItem.obtain(intent, ident, activityInfo,
config(), overrideConfig, compat, referrer, null /* voiceInteractor */,
@@ -352,7 +352,8 @@
ClientTransaction transaction = ClientTransaction.obtain(new StubAppThread(),
null /* activityToken */);
transaction.addCallback(FixedRotationAdjustmentsItem.obtain(new Binder(),
- new FixedRotationAdjustments(Surface.ROTATION_270, DisplayCutout.NO_CUTOUT)));
+ new FixedRotationAdjustments(Surface.ROTATION_270, 1920, 1080,
+ DisplayCutout.NO_CUTOUT)));
writeAndPrepareForReading(transaction);
diff --git a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
index 2fc42e9..3cf1722 100644
--- a/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
+++ b/core/tests/coretests/src/android/view/DisplayAdjustmentsTests.java
@@ -77,8 +77,10 @@
final int realRotation = Surface.ROTATION_0;
final int fixedRotation = Surface.ROTATION_90;
- mDisplayAdjustments.setFixedRotationAdjustments(
- new FixedRotationAdjustments(fixedRotation, null /* cutout */));
+ final int appWidth = 1080;
+ final int appHeight = 1920;
+ mDisplayAdjustments.setFixedRotationAdjustments(new FixedRotationAdjustments(
+ fixedRotation, appWidth, appHeight, null /* cutout */));
final int w = 1000;
final int h = 2000;
@@ -95,13 +97,21 @@
metrics.heightPixels = metrics.noncompatHeightPixels = h;
final DisplayMetrics flippedMetrics = new DisplayMetrics();
- flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = h;
+ // The physical dpi should not be adjusted.
+ flippedMetrics.xdpi = flippedMetrics.noncompatXdpi = w;
flippedMetrics.widthPixels = flippedMetrics.noncompatWidthPixels = h;
- flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = w;
+ flippedMetrics.ydpi = flippedMetrics.noncompatYdpi = h;
flippedMetrics.heightPixels = flippedMetrics.noncompatHeightPixels = w;
mDisplayAdjustments.adjustMetrics(metrics, realRotation);
assertEquals(flippedMetrics, metrics);
+
+ mDisplayAdjustments.adjustGlobalAppMetrics(metrics);
+
+ assertEquals(appWidth, metrics.widthPixels);
+ assertEquals(appWidth, metrics.noncompatWidthPixels);
+ assertEquals(appHeight, metrics.heightPixels);
+ assertEquals(appHeight, metrics.noncompatHeightPixels);
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index afab769..8a000a0 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -124,6 +124,22 @@
}
@Test
+ public void testCalculateInsets_extraNavRightClimateTop() throws Exception {
+ mState.getSource(ITYPE_CLIMATE_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_CLIMATE_BAR).setVisible(true);
+ mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+ mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
+ WINDOWING_MODE_UNDEFINED, null);
+ // ITYPE_CLIMATE_BAR is a type of status bar and ITYPE_EXTRA_NAVIGATION_BAR is a type
+ // of navigation bar.
+ assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
+ assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
+ }
+
+ @Test
public void testCalculateInsets_imeIgnoredWithoutAdjustResize() {
mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
@@ -336,6 +352,8 @@
public void testGetDefaultVisibility() {
assertTrue(InsetsState.getDefaultVisibility(ITYPE_STATUS_BAR));
assertTrue(InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR));
+ assertTrue(InsetsState.getDefaultVisibility(ITYPE_CLIMATE_BAR));
+ assertTrue(InsetsState.getDefaultVisibility(ITYPE_EXTRA_NAVIGATION_BAR));
assertTrue(InsetsState.getDefaultVisibility(ITYPE_CAPTION_BAR));
assertFalse(InsetsState.getDefaultVisibility(ITYPE_IME));
}
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 5c41087..92fb528 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -17,16 +17,23 @@
package android.view.inputmethod;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyInt;
import android.annotation.Nullable;
+import android.graphics.BlurMaskFilter;
import android.os.Parcel;
import android.os.UserHandle;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
+import android.text.style.MaskFilterSpan;
+import android.text.style.UnderlineSpan;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -280,6 +287,55 @@
editorInfo.getInitialTextBeforeCursor(/* length= */ 60, /* flags= */ 1);
}
+ @Test
+ public void testSpanAfterSurroundingTextRetrieval() {
+ final int flags = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE;
+ final SpannableStringBuilder sb =
+ new SpannableStringBuilder("ParcelableSpan and non-ParcelableSpan test");
+ final int parcelableStart = 0;
+ final int parcelableEnd = 14;
+ final int nonParcelableStart = 19;
+ final int nonParcelableEnd = 37;
+ final UnderlineSpan parcelableSpan = new UnderlineSpan();
+ final MaskFilterSpan nonParcelableSpan =
+ new MaskFilterSpan(new BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL));
+
+ // Set spans
+ sb.setSpan(parcelableSpan, parcelableStart, parcelableEnd, flags);
+ sb.setSpan(nonParcelableSpan, nonParcelableStart, nonParcelableEnd, flags);
+
+ Object[] spansBefore = sb.getSpans(/* queryStart= */ 0, sb.length(), Object.class);
+ Object[] parcelableSpanBefore = sb.getSpans(parcelableStart, parcelableEnd, Object.class);
+
+ // Verify the original spans length is 2, include ParcelableSpan and non-ParcelableSpan.
+ assertNotNull(spansBefore);
+ assertEquals(2, spansBefore.length);
+
+ // Set initial surrounding text then retrieve the text.
+ EditorInfo editorInfo = new EditorInfo();
+ editorInfo.initialSelStart = sb.length();
+ editorInfo.initialSelEnd = sb.length();
+ editorInfo.inputType = EditorInfo.TYPE_CLASS_TEXT;
+ editorInfo.setInitialSurroundingText(sb);
+ SpannableString textBeforeCursor =
+ (SpannableString) editorInfo.getInitialTextBeforeCursor(
+ /* length= */ 60, /* flags= */ 1);
+
+ Object[] spansAfter =
+ textBeforeCursor.getSpans(/* queryStart= */ 0, sb.length(), Object.class);
+ Object[] parcelableSpanAfter =
+ textBeforeCursor.getSpans(parcelableStart, parcelableEnd, Object.class);
+ Object[] nonParcelableSpanAfter =
+ textBeforeCursor.getSpans(nonParcelableStart, nonParcelableEnd, Object.class);
+
+ // Verify only remain ParcelableSpan and it's different from the original Span instance.
+ assertNotNull(spansAfter);
+ assertEquals(1, spansAfter.length);
+ assertEquals(1, parcelableSpanAfter.length);
+ assertEquals(0, nonParcelableSpanAfter.length);
+ assertNotEquals(parcelableSpanBefore, parcelableSpanAfter);
+ }
+
private static void assertExpectedTextLength(EditorInfo editorInfo,
@Nullable Integer expectBeforeCursorLength, @Nullable Integer expectSelectionLength,
@Nullable Integer expectAfterCursorLength) {
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java b/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
deleted file mode 100644
index 936c999..0000000
--- a/core/tests/coretests/src/android/widget/focus/ListOfEditTexts.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget.focus;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.Bundle;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-
-import com.google.android.collect.Lists;
-
-import java.util.List;
-
-public class ListOfEditTexts extends Activity {
-
- private int mLinesPerEditText = 12;
-
- private ListView mListView;
- private LinearLayout mLinearLayout;
-
- public ListView getListView() {
- return mListView;
- }
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // create linear layout
- mLinearLayout = new LinearLayout(this);
- mLinearLayout.setOrientation(LinearLayout.VERTICAL);
- mLinearLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
-
- // add a button above
- Button buttonAbove = new Button(this);
- buttonAbove.setLayoutParams(
- new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
- buttonAbove.setText("button above list");
- mLinearLayout.addView(buttonAbove);
-
- // add a list view to it
- mListView = new ListView(this);
- mListView.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
- mListView.setDrawSelectorOnTop(false);
- mListView.setItemsCanFocus(true);
- mListView.setLayoutParams((new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- 0,
- 1f)));
-
- List<String> bodies = Lists.newArrayList(
- getBody("zero hello, my name is android"),
- getBody("one i'm a paranoid android"),
- getBody("two i robot. huh huh."),
- getBody("three not the g-phone!"));
-
- mListView.setAdapter(new MyAdapter(this, bodies));
- mLinearLayout.addView(mListView);
-
- // add button below
- Button buttonBelow = new Button(this);
- buttonBelow.setLayoutParams(
- new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
- buttonBelow.setText("button below list");
- mLinearLayout.addView(buttonBelow);
-
- setContentView(mLinearLayout);
- }
-
- String getBody(String line) {
- StringBuilder sb = new StringBuilder((line.length() + 5) * mLinesPerEditText);
- for (int i = 0; i < mLinesPerEditText; i++) {
- sb.append(i + 1).append(' ').append(line);
- if (i < mLinesPerEditText - 1) {
- sb.append('\n'); // all but last line
- }
- }
- return sb.toString();
- }
-
-
- private static class MyAdapter extends ArrayAdapter<String> {
-
- public MyAdapter(Context context, List<String> bodies) {
- super(context, 0, bodies);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- String body = getItem(position);
-
- if (convertView != null) {
- ((EditText) convertView).setText(body);
- return convertView;
- }
-
- EditText editText = new EditText(getContext());
- editText.setText(body);
- return editText;
- }
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index ece5037..e17800f 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -16,7 +16,7 @@
package com.android.internal.jank;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.google.common.truth.Truth.assertThat;
@@ -75,20 +75,13 @@
doNothing().when(mRenderer).addObserver(any());
doNothing().when(mRenderer).removeObserver(any());
- Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
- mTracker = Mockito.spy(new FrameTracker(session, handler, mRenderer, mWrapper));
+ Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ mTracker = Mockito.spy(
+ new FrameTracker(session, handler, mRenderer, mWrapper));
doNothing().when(mTracker).triggerPerfetto();
}
@Test
- public void testIgnoresSecondBegin() {
- // Observer should be only added once in continuous calls.
- mTracker.begin();
- mTracker.begin();
- verify(mRenderer, only()).addObserver(any());
- }
-
- @Test
public void testOnlyFirstFrameOverThreshold() {
// Just provide current timestamp anytime mWrapper asked for VSYNC_TIMESTAMP
when(mWrapper.getMetric(FrameMetrics.VSYNC_TIMESTAMP))
@@ -170,6 +163,29 @@
verify(mTracker).triggerPerfetto();
}
+ @Test
+ public void testBeginCancel() {
+ mTracker.begin();
+ verify(mRenderer).addObserver(any());
+
+ // First frame - not janky
+ setupFirstFrameMockWithDuration(4);
+ mTracker.onFrameMetricsAvailable(0);
+
+ // normal frame - not janky
+ setupOtherFrameMockWithDuration(12);
+ mTracker.onFrameMetricsAvailable(0);
+
+ // a janky frame
+ setupOtherFrameMockWithDuration(30);
+ mTracker.onFrameMetricsAvailable(0);
+
+ mTracker.cancel();
+ verify(mRenderer).removeObserver(any());
+ // Since the tracker has been cancelled, shouldn't trigger perfetto.
+ verify(mTracker, never()).triggerPerfetto();
+ }
+
private void setupFirstFrameMockWithDuration(long durationMillis) {
doReturn(1L).when(mWrapper).getMetric(FrameMetrics.FIRST_DRAW_FRAME);
doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis))
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index 5c0b0c9..b669cc6 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -16,15 +16,20 @@
package com.android.internal.jank;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_GESTURE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.Message;
import android.view.View;
import android.view.ViewAttachTestActivity;
@@ -38,17 +43,13 @@
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.mockito.Mockito;
-import org.testng.Assert;
-
-import java.util.HashMap;
-import java.util.Map;
+import org.mockito.ArgumentCaptor;
@SmallTest
public class InteractionJankMonitorTest {
private ViewAttachTestActivity mActivity;
private View mView;
- private FrameTracker mTracker;
+ private HandlerThread mWorker;
@Rule
public ActivityTestRule<ViewAttachTestActivity> mRule =
@@ -61,55 +62,52 @@
mView = mActivity.getWindow().getDecorView();
assertThat(mView.isAttachedToWindow()).isTrue();
- InteractionJankMonitor.reset();
+ InteractionJankMonitor.abandon();
- // Prepare a FrameTracker to inject.
- Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
- FrameMetricsWrapper wrapper = Mockito.spy(new FrameTracker.FrameMetricsWrapper());
- ThreadedRendererWrapper renderer =
- Mockito.spy(new ThreadedRendererWrapper(mView.getThreadedRenderer()));
- Handler handler = mActivity.getMainThreadHandler();
- mTracker = Mockito.spy(new FrameTracker(session, handler, renderer, wrapper));
+ Handler handler = spy(new Handler(mActivity.getMainLooper()));
+ doReturn(true).when(handler).sendMessageAtTime(any(), anyLong());
+ mWorker = spy(new HandlerThread("Interaction-jank-monitor-test"));
+ doNothing().when(mWorker).start();
+ doReturn(handler).when(mWorker).getThreadHandler();
}
@Test
public void testBeginEnd() {
- // Should throw exception if the view is not attached.
- Assert.assertThrows(IllegalStateException.class,
- () -> InteractionJankMonitor.init(new View(mActivity)));
+ // Should return false if the view is not attached.
+ InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
+ assertThat(monitor.init(new View(mActivity))).isFalse();
// Verify we init InteractionJankMonitor correctly.
- Map<String, FrameTracker> map = new HashMap<>();
- HandlerThread worker = Mockito.spy(new HandlerThread("Aot-test"));
- doNothing().when(worker).start();
- InteractionJankMonitor.init(mView, mView.getThreadedRenderer(), map, worker);
- verify(worker).start();
+ assertThat(monitor.init(mView)).isTrue();
+ verify(mWorker).start();
+
+ Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
+ new ThreadedRendererWrapper(mView.getThreadedRenderer()),
+ new FrameMetricsWrapper()));
+ doReturn(tracker).when(monitor).createFrameTracker(any());
// Simulate a trace session and see if begin / end are invoked.
- Session session = new Session(CUJ_NOTIFICATION_SHADE_GESTURE);
- assertThat(map.get(session.getName())).isNull();
- InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE, mTracker);
- verify(mTracker).begin();
- assertThat(map.get(session.getName())).isEqualTo(mTracker);
- InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE);
- verify(mTracker).end();
- assertThat(map.get(session.getName())).isNull();
+ assertThat(monitor.begin(session.getCuj())).isTrue();
+ verify(tracker).begin();
+ assertThat(monitor.end(session.getCuj())).isTrue();
+ verify(tracker).end();
}
@Test
public void testCheckInitState() {
- // Should throw exception if invoking begin / end without init invocation.
- Assert.assertThrows(IllegalStateException.class,
- () -> InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE));
- Assert.assertThrows(IllegalStateException.class,
- () -> InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE));
+ InteractionJankMonitor monitor = new InteractionJankMonitor(mWorker);
+
+ // Should return false if invoking begin / end without init invocation.
+ assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
+ assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isFalse();
// Everything should be fine if invoking init first.
boolean thrown = false;
try {
- InteractionJankMonitor.init(mActivity.getWindow().getDecorView());
- InteractionJankMonitor.begin(CUJ_NOTIFICATION_SHADE_GESTURE);
- InteractionJankMonitor.end(CUJ_NOTIFICATION_SHADE_GESTURE);
+ monitor.init(mView);
+ assertThat(monitor.begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
+ assertThat(monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE)).isTrue();
} catch (Exception ex) {
thrown = true;
} finally {
@@ -117,4 +115,27 @@
}
}
+ @Test
+ public void testBeginCancel() {
+ InteractionJankMonitor monitor = spy(new InteractionJankMonitor(mWorker));
+
+ ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+ assertThat(monitor.init(mView)).isTrue();
+
+ Session session = new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(),
+ new ThreadedRendererWrapper(mView.getThreadedRenderer()),
+ new FrameMetricsWrapper()));
+ doReturn(tracker).when(monitor).createFrameTracker(any());
+
+ assertThat(monitor.begin(session.getCuj())).isTrue();
+ verify(tracker).begin();
+ verify(mWorker.getThreadHandler()).sendMessageAtTime(captor.capture(), anyLong());
+ Runnable runnable = captor.getValue().getCallback();
+ assertThat(runnable).isNotNull();
+ mWorker.getThreadHandler().removeCallbacks(runnable);
+ runnable.run();
+ verify(tracker).cancel();
+ }
+
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
index 22c41f3..85f9c97 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
@@ -60,7 +60,7 @@
stat1.cpuTimeMicros = 1000;
callStats.add(stat1);
- bi.noteBinderCallStats(workSourceUid, 42, callStats, null);
+ bi.noteBinderCallStats(workSourceUid, 42, callStats);
callStats.clear();
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
@@ -70,7 +70,7 @@
stat2.cpuTimeMicros = 500;
callStats.add(stat2);
- bi.noteBinderCallStats(workSourceUid, 8, callStats, null);
+ bi.noteBinderCallStats(workSourceUid, 8, callStats);
BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
assertEquals(42 + 8, uid.getBinderCallCount());
@@ -112,7 +112,7 @@
stat1b.cpuTimeMicros = 1500;
callStats.add(stat1b);
- bi.noteBinderCallStats(workSourceUid1, 65, callStats, null);
+ bi.noteBinderCallStats(workSourceUid1, 65, callStats);
// No recorded stats for some methods. Must use the global average.
callStats.clear();
@@ -121,11 +121,11 @@
stat2.incrementalCallCount = 10;
callStats.add(stat2);
- bi.noteBinderCallStats(workSourceUid2, 40, callStats, null);
+ bi.noteBinderCallStats(workSourceUid2, 40, callStats);
// No stats for any calls. Must use the global average
callStats.clear();
- bi.noteBinderCallStats(workSourceUid3, 50, callStats, null);
+ bi.noteBinderCallStats(workSourceUid3, 50, callStats);
bi.updateSystemServiceCallStats();
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 96250db..0eb34a9 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -47,8 +47,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Random;
+import java.util.Set;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -770,13 +772,27 @@
bcs.setSamplingInterval(1);
bcs.setTrackScreenInteractive(false);
+
final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
- bcs.setCallStatsObserver(
- (workSourceUid, incrementalCallCount, callStats, binderThreadIds) ->
- callStatsList.addAll(callStats));
+ final Set<Integer> nativeTids = new HashSet<>();
+ bcs.setCallStatsObserver(new BinderInternal.CallStatsObserver() {
+ @Override
+ public void noteCallStats(int workSourceUid, long incrementalCallCount,
+ Collection<BinderCallsStats.CallStat> callStats) {
+ callStatsList.addAll(callStats);
+ }
+
+ @Override
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ for (int tid : binderThreadNativeTids) {
+ nativeTids.add(tid);
+ }
+ }
+ });
Binder binder = new Binder();
+ bcs.nativeTid = 1000;
CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
bcs.time += 10;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
@@ -785,6 +801,7 @@
bcs.time += 20;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+ bcs.nativeTid = 2000;
callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
bcs.time += 30;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
@@ -809,6 +826,10 @@
assertEquals(30, callStats.maxCpuTimeMicros);
}
}
+
+ assertEquals(2, nativeTids.size());
+ assertTrue(nativeTids.contains(1000));
+ assertTrue(nativeTids.contains(2000));
}
@Test
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index ac5443e..2eee140 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -87,7 +87,7 @@
stat1.cpuTimeMicros = 1000000;
callStats.add(stat1);
- mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null);
+ mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats);
callStats.clear();
BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2,
@@ -97,7 +97,7 @@
stat2.cpuTimeMicros = 9000000;
callStats.add(stat2);
- mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null);
+ mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats);
mMockBatteryStats.updateSystemServiceCallStats();
mMockBatteryStats.updateSystemServerThreadStats();
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 5ac610b..d266cdb 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -85,7 +85,7 @@
@MediumTest
@Presubmit
public class ActivityThreadClientTest {
- private static final long WAIT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
+ private static final long WAIT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
@Test
@UiThreadTest
diff --git a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
index 55d186c..469a4cc 100644
--- a/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/CharSequencesTest.java
@@ -16,16 +16,17 @@
package com.android.internal.util;
-import com.android.internal.util.CharSequences;
import static com.android.internal.util.CharSequences.forAsciiBytes;
-import junit.framework.TestCase;
+
import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
public class CharSequencesTest extends TestCase {
@SmallTest
public void testCharSequences() {
- String s = "Crazy Bob";
+ String s = "Hello Bob";
byte[] bytes = s.getBytes();
String copy = toString(forAsciiBytes(bytes));
@@ -34,11 +35,11 @@
copy = toString(forAsciiBytes(bytes, 0, s.length()));
assertTrue(s.equals(copy));
- String crazy = toString(forAsciiBytes(bytes, 0, 5));
- assertTrue("Crazy".equals(crazy));
+ String hello = toString(forAsciiBytes(bytes, 0, 5));
+ assertTrue("Hello".equals(hello));
- String a = toString(forAsciiBytes(bytes, 0, 3).subSequence(2, 3));
- assertTrue("a".equals(a));
+ String l = toString(forAsciiBytes(bytes, 0, 3).subSequence(2, 3));
+ assertTrue("l".equals(l));
String empty = toString(forAsciiBytes(bytes, 0, 3).subSequence(3, 3));
assertTrue("".equals(empty));
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 0a06814..10c2b09 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -153,8 +153,8 @@
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />
<assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" />
+
<assign-permission name="android.permission.INTERNET" uid="media" />
- <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="media" />
<assign-permission name="android.permission.INTERNET" uid="shell" />
@@ -164,7 +164,6 @@
<assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="audioserver" />
- <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="audioserver" />
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
@@ -175,10 +174,8 @@
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" />
<assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" />
<assign-permission name="android.permission.MANAGE_APP_OPS_MODES" uid="cameraserver" />
- <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="cameraserver" />
<assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
- <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="graphics" />
<assign-permission name="android.permission.DUMP" uid="incidentd" />
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" />
@@ -193,10 +190,8 @@
<assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
<assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
<assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="statsd" />
- <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="statsd" />
<assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="gpu_service" />
- <assign-permission name="android.permission.INTERACT_ACROSS_USERS" uid="gpu_service" />
<split-permission name="android.permission.ACCESS_FINE_LOCATION">
<new-permission name="android.permission.ACCESS_COARSE_LOCATION" />
@@ -234,8 +229,7 @@
<library name="android.test.base"
file="/system/framework/android.test.base.jar" />
<library name="android.test.mock"
- file="/system/framework/android.test.mock.jar"
- dependency="android.test.base" />
+ file="/system/framework/android.test.mock.jar" />
<library name="android.test.runner"
file="/system/framework/android.test.runner.jar"
dependency="android.test.base:android.test.mock" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6b80bb6..86e7adf 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -151,6 +151,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1910833551": {
+ "message": "SyncSet{%x:%d} Start for %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-1895337367": {
"message": "Delete root task display=%d winMode=%d",
"level": "VERBOSE",
@@ -463,6 +469,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1387080937": {
+ "message": "SyncSet{%x:%d} Child ready, now ready=%b and waiting on %d transactions",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-1364754753": {
"message": "Task vanished taskId=%d",
"level": "VERBOSE",
@@ -481,6 +493,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1340783230": {
+ "message": "SyncSet{%x:%d} Added %s. now waiting on %d transactions",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-1340540100": {
"message": "Creating SnapshotStartingData",
"level": "VERBOSE",
@@ -859,6 +877,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DragState.java"
},
+ "-678300709": {
+ "message": "SyncSet{%x:%d} Trying to add %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"-668956537": {
"message": " THUMBNAIL %s: CREATE",
"level": "INFO",
@@ -1735,6 +1759,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
+ "590184240": {
+ "message": "- NOT adding to sync: visible=%b hasListener=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/WindowContainer.java"
+ },
"594260577": {
"message": "createWallpaperAnimations()",
"level": "DEBUG",
@@ -2005,6 +2035,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "1000601037": {
+ "message": "SyncSet{%x:%d} Set ready",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"1001904964": {
"message": "***** BOOT TIMEOUT: forcing display enabled",
"level": "WARN",
@@ -2599,6 +2635,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "2001924866": {
+ "message": "SyncSet{%x:%d} Finished. Reporting %d containers to %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_SYNC_ENGINE",
+ "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
+ },
"2016061474": {
"message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
"level": "VERBOSE",
@@ -2775,6 +2817,9 @@
"WM_DEBUG_SWITCH": {
"tag": "WindowManager"
},
+ "WM_DEBUG_SYNC_ENGINE": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_WINDOW_MOVEMENT": {
"tag": "WindowManager"
},
diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp
index 3a3bea4..ee5c5b0 100644
--- a/data/fonts/Android.bp
+++ b/data/fonts/Android.bp
@@ -17,9 +17,8 @@
src: "DroidSansMono.ttf",
required: [
// Roboto-Regular.ttf provides DroidSans.ttf as a symlink to itself
+ // Roboto-Regular.ttf provides DroidSans-Bold.ttf as a symlink to itself
"Roboto-Regular.ttf",
- // Roboto-Bold.ttf provides DroidSans-Bold.ttf as a symlink to itself
- "Roboto-Bold.ttf",
],
}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index c992bd8..0504cdc 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -18,23 +18,104 @@
prefer the former when an 800 weight is requested. Since bold spans
effectively add 300 to the weight, this ensures that 900 is the bold
paired with the 500 weight, ensuring adequate contrast.
+
+ TODO(rsheeter) update comment; ordering to match 800 to 900 is no longer required
-->
<familyset version="23">
<!-- first font is default -->
<family name="sans-serif">
- <font weight="100" style="normal">Roboto-Thin.ttf</font>
- <font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
- <font weight="300" style="normal">Roboto-Light.ttf</font>
- <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
- <font weight="400" style="normal">Roboto-Regular.ttf</font>
- <font weight="400" style="italic">Roboto-Italic.ttf</font>
- <font weight="500" style="normal">Roboto-Medium.ttf</font>
- <font weight="500" style="italic">Roboto-MediumItalic.ttf</font>
- <font weight="900" style="normal">Roboto-Black.ttf</font>
- <font weight="900" style="italic">Roboto-BlackItalic.ttf</font>
- <font weight="700" style="normal">Roboto-Bold.ttf</font>
- <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
- </family>
+ <font weight="100" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ <font weight="100" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="100" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ </family>
+
<!-- Note that aliases must come after the fonts they reference. -->
<alias name="sans-serif-thin" to="sans-serif" weight="100" />
@@ -47,14 +128,96 @@
<alias name="verdana" to="sans-serif" />
<family name="sans-serif-condensed">
- <font weight="300" style="normal">RobotoCondensed-Light.ttf</font>
- <font weight="300" style="italic">RobotoCondensed-LightItalic.ttf</font>
- <font weight="400" style="normal">RobotoCondensed-Regular.ttf</font>
- <font weight="400" style="italic">RobotoCondensed-Italic.ttf</font>
- <font weight="500" style="normal">RobotoCondensed-Medium.ttf</font>
- <font weight="500" style="italic">RobotoCondensed-MediumItalic.ttf</font>
- <font weight="700" style="normal">RobotoCondensed-Bold.ttf</font>
- <font weight="700" style="italic">RobotoCondensed-BoldItalic.ttf</font>
+ <font weight="100" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="normal">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="0" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
+ <font weight="100" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="100" />
+ </font>
+ <font weight="200" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="200" />
+ </font>
+ <font weight="300" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="300" />
+ </font>
+ <font weight="400" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="800" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="800" />
+ </font>
+ <font weight="900" style="italic">Roboto-Regular.ttf
+ <axis tag="ital" stylevalue="1" />
+ <axis tag="wdth" stylevalue="75" />
+ <axis tag="wght" stylevalue="900" />
+ </font>
</family>
<alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
<alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" />
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 4c7e960..4cd55e8 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -24,7 +24,7 @@
*/
public final class BLASTBufferQueue {
// Note: This field is accessed by native code.
- private long mNativeObject; // BLASTBufferQueue*
+ public long mNativeObject; // BLASTBufferQueue*
private static native long nativeCreate(long surfaceControl, long width, long height,
boolean tripleBufferingEnabled);
@@ -41,9 +41,13 @@
public void destroy() {
nativeDestroy(mNativeObject);
+ mNativeObject = 0;
}
- public Surface getSurface() {
+ /**
+ * @return a new Surface instance from the IGraphicsBufferProducer of the adapter.
+ */
+ public Surface createSurface() {
return nativeGetSurface(mNativeObject);
}
diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java
index 779a890..3bc8119 100644
--- a/graphics/java/android/graphics/BlurShader.java
+++ b/graphics/java/android/graphics/BlurShader.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.NonNull;
import android.annotation.Nullable;
/**
@@ -28,6 +29,7 @@
private final float mRadiusX;
private final float mRadiusY;
private final Shader mInputShader;
+ private final TileMode mEdgeTreatment;
private long mNativeInputShader = 0;
@@ -35,22 +37,42 @@
* Create a {@link BlurShader} that blurs the contents of the optional input shader
* with the specified radius along the x and y axis. If no input shader is provided
* then all drawing commands issued with a {@link android.graphics.Paint} that this
+ * shader is installed in will be blurred.
+ *
+ * This uses a default {@link TileMode#DECAL} for edge treatment
+ *
+ * @param radiusX Radius of blur along the X axis
+ * @param radiusY Radius of blur along the Y axis
+ * @param inputShader Input shader that provides the content to be blurred
+ */
+ public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) {
+ this(radiusX, radiusY, inputShader, TileMode.DECAL);
+ }
+
+ /**
+ * Create a {@link BlurShader} that blurs the contents of the optional input shader
+ * with the specified radius along the x and y axis. If no input shader is provided
+ * then all drawing commands issued with a {@link android.graphics.Paint} that this
* shader is installed in will be blurred
* @param radiusX Radius of blur along the X axis
* @param radiusY Radius of blur along the Y axis
* @param inputShader Input shader that provides the content to be blurred
+ * @param edgeTreatment Policy for how to blur content near edges of the blur shader
*/
- public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) {
+ public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader,
+ @NonNull TileMode edgeTreatment) {
mRadiusX = radiusX;
mRadiusY = radiusY;
mInputShader = inputShader;
+ mEdgeTreatment = edgeTreatment;
}
/** @hide **/
@Override
protected long createNativeInstance(long nativeMatrix) {
mNativeInputShader = mInputShader != null ? mInputShader.getNativeInstance() : 0;
- return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader);
+ return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader,
+ mEdgeTreatment.nativeInt);
}
/** @hide **/
@@ -61,5 +83,5 @@
}
private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY,
- long inputShader);
+ long inputShader, int edgeTreatment);
}
diff --git a/graphics/java/android/graphics/ParcelableColorSpace.java b/graphics/java/android/graphics/ParcelableColorSpace.java
index f9033a5..d408ac3 100644
--- a/graphics/java/android/graphics/ParcelableColorSpace.java
+++ b/graphics/java/android/graphics/ParcelableColorSpace.java
@@ -25,8 +25,6 @@
* A {@link Parcelable} {@link ColorSpace}. In order to enable parceling, the ColorSpace
* must be either a {@link ColorSpace.Named Named} ColorSpace or a {@link ColorSpace.Rgb} instance
* that has an ICC parametric transfer function as returned by {@link Rgb#getTransferParameters()}.
- * TODO: Make public
- * @hide
*/
public final class ParcelableColorSpace extends ColorSpace implements Parcelable {
private final ColorSpace mColorSpace;
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 8154ebf..d71ff11 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -95,7 +95,11 @@
* repeat the shader's image horizontally and vertically, alternating
* mirror images so that adjacent images always seam
*/
- MIRROR (2);
+ MIRROR(2),
+ /**
+ * Only draw within the original domain, return transparent-black everywhere else
+ */
+ DECAL(3);
TileMode(int nativeInt) {
this.nativeInt = nativeInt;
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index b09082e..cbae675 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -22,6 +22,8 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.graphics.Paint;
+import android.graphics.RectF;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
import android.util.TypedValue;
@@ -29,6 +31,7 @@
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
import libcore.util.NativeAllocationRegistry;
@@ -63,6 +66,7 @@
private @Nullable ByteBuffer mBuffer;
private @Nullable File mFile;
+ private @Nullable Font mFont;
private @NonNull String mLocaleList = "";
private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED;
private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED;
@@ -204,6 +208,22 @@
}
/**
+ * Constructs a builder from existing Font instance.
+ *
+ * @param font the font instance.
+ */
+ public Builder(@NonNull Font font) {
+ mFont = font;
+ // Copies all parameters as a default value.
+ mBuffer = font.getBuffer();
+ mWeight = font.getStyle().getWeight();
+ mItalic = font.getStyle().getSlant();
+ mAxes = font.getAxes();
+ mFile = font.getFile();
+ mTtcIndex = font.getTtcIndex();
+ }
+
+ /**
* Creates a buffer containing font data using the assetManager and other
* provided inputs.
*
@@ -430,8 +450,13 @@
}
final ByteBuffer readonlyBuffer = mBuffer.asReadOnlyBuffer();
final String filePath = mFile == null ? "" : mFile.getAbsolutePath();
- final long ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic,
- mTtcIndex);
+
+ long ptr;
+ if (mFont == null) {
+ ptr = nBuild(builderPtr, readonlyBuffer, filePath, mWeight, italic, mTtcIndex);
+ } else {
+ ptr = nClone(mFont.getNativePtr(), builderPtr, mWeight, italic, mTtcIndex);
+ }
final Font font = new Font(ptr, readonlyBuffer, mFile,
new FontStyle(mWeight, slant), mTtcIndex, mAxes, mLocaleList);
sFontRegistry.registerNativeAllocation(font, ptr);
@@ -449,6 +474,10 @@
boolean italic, int ttcIndex);
@CriticalNative
private static native long nGetReleaseNativeFont();
+
+ @FastNative
+ private static native long nClone(long fontPtr, long builderPtr, int weight,
+ boolean italic, int ttcIndex);
}
private final long mNativePtr; // address of the shared ptr of minikin::Font
@@ -538,6 +567,40 @@
return LocaleList.forLanguageTags(mLocaleList);
}
+ /**
+ * Retrieve the glyph horizontal advance and bounding box.
+ *
+ * Note that {@link android.graphics.Typeface} in {@link android.graphics.Paint} is ignored.
+ *
+ * @param glyphId a glyph ID
+ * @param paint a paint object used for resolving glyph style
+ * @param rect a nullable destination object. If null is passed, this function just return the
+ * horizontal advance. If non-null is passed, this function fills bounding box
+ * information to this object.
+ * @return the amount of horizontal advance in pixels
+ */
+ public float getGlyphBounds(@IntRange(from = 0) int glyphId, @NonNull Paint paint,
+ @Nullable RectF rect) {
+ return nGetGlyphBounds(mNativePtr, glyphId, paint.getNativeInstance(), rect);
+ }
+
+ /**
+ * Retrieve the font metrics information.
+ *
+ * Note that {@link android.graphics.Typeface} in {@link android.graphics.Paint} is ignored.
+ *
+ * @param paint a paint object used for retrieving font metrics.
+ * @param metrics a nullable destination object. If null is passed, this function only retrieve
+ * recommended interline spacing. If non-null is passed, this function fills to
+ * font metrics to it.
+ *
+ * @see Paint#getFontMetrics()
+ * @see Paint#getFontMetricsInt()
+ */
+ public void getMetrics(@NonNull Paint paint, @Nullable Paint.FontMetrics metrics) {
+ nGetFontMetrics(mNativePtr, paint.getNativeInstance(), metrics);
+ }
+
/** @hide */
public long getNativePtr() {
return mNativePtr;
@@ -573,4 +636,10 @@
+ ", buffer=" + mBuffer
+ "}";
}
+
+ @FastNative
+ private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
+
+ @FastNative
+ private static native float nGetFontMetrics(long font, long paint, Paint.FontMetrics metrics);
}
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index b6668fb..da5965d 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -93,4 +93,8 @@
<string name="one_handed_tutorial_title">Using one-handed mode</string>
<!-- One-Handed Tutorial description [CHAR LIMIT=NONE] -->
<string name="one_handed_tutorial_description">To exit, swipe up from the bottom of the screen or tap anywhere above the app</string>
+ <!-- Accessibility description for start one-handed mode [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_start_one_handed">Start one-handed mode</string>
+ <!-- Accessibility description for stop one-handed mode [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_stop_one_handed">Exit one-handed mode</string>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
index b15b515..7c0c738 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTutorialHandler.java
@@ -29,6 +29,8 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
@@ -50,12 +52,16 @@
private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
private final Rect mLastUpdatedBounds = new Rect();
private final WindowManager mWindowManager;
+ private final AccessibilityManager mAccessibilityManager;
+ private final String mPackageName;
private View mTutorialView;
private Point mDisplaySize = new Point();
private Handler mUpdateHandler;
private ContentResolver mContentResolver;
private boolean mCanShowTutorial;
+ private String mStartOneHandedDescription;
+ private String mStopOneHandedDescription;
/**
* Container of the tutorial panel showing at outside region when one handed starting
@@ -72,9 +78,12 @@
public OneHandedTutorialHandler(Context context) {
context.getDisplay().getRealSize(mDisplaySize);
+ mPackageName = context.getPackageName();
mContentResolver = context.getContentResolver();
mUpdateHandler = new Handler();
mWindowManager = context.getSystemService(WindowManager.class);
+ mAccessibilityManager = (AccessibilityManager)
+ context.getSystemService(Context.ACCESSIBILITY_SERVICE);
mTargetViewContainer = new FrameLayout(context);
mTargetViewContainer.setClipChildren(false);
mTutorialAreaHeight = Math.round(mDisplaySize.y
@@ -84,6 +93,10 @@
mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
? false : true;
+ mStartOneHandedDescription = context.getResources().getString(
+ R.string.accessibility_action_start_one_handed);
+ mStopOneHandedDescription = context.getResources().getString(
+ R.string.accessibility_action_stop_one_handed);
if (mCanShowTutorial) {
createOrUpdateTutorialTarget();
}
@@ -94,13 +107,16 @@
mUpdateHandler.post(() -> {
updateFinished(View.VISIBLE, 0f);
updateTutorialCount();
+ announcementForScreenReader(true);
});
}
@Override
public void onStopFinished(Rect bounds) {
- mUpdateHandler.post(() -> updateFinished(
- View.INVISIBLE, -mTargetViewContainer.getHeight()));
+ mUpdateHandler.post(() -> {
+ updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
+ announcementForScreenReader(false);
+ });
}
private void updateFinished(int visible, float finalPosition) {
@@ -121,6 +137,17 @@
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount);
}
+ private void announcementForScreenReader(boolean isStartOneHanded) {
+ if (mAccessibilityManager.isTouchExplorationEnabled()) {
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setPackageName(mPackageName);
+ event.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
+ event.getText().add(isStartOneHanded
+ ? mStartOneHandedDescription : mStopOneHandedDescription);
+ mAccessibilityManager.sendAccessibilityEvent(event);
+ }
+ }
+
/**
* Adds the tutorial target view to the WindowManager and update its layout, so it's ready
* to be animated in.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
index 00146e9..edbbd69 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/DividerView.java
@@ -1319,10 +1319,6 @@
mBackground.getRight(), mBackground.getBottom(), Op.UNION);
}
- void onDockedFirstAnimationFrame() {
- saveSnapTargetBeforeMinimized(mSplitLayout.getSnapAlgorithm().getMiddleTarget());
- }
-
void onDockedTopTask() {
mState.animateAfterRecentsDrawn = true;
startDragging(false /* animate */, false /* touching */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 184342f..58106c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -68,9 +68,6 @@
/** Called when there's a task undocking. */
void onUndockingTask();
- /** Called when the first docked animation frame rendered. */
- void onDockedFirstAnimationFrame();
-
/** Called when top task docked. */
void onDockedTopTask();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index eed5092..c8be937 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -426,13 +426,6 @@
}
}
- /** Called when the first docked animation frame rendered. */
- public void onDockedFirstAnimationFrame() {
- if (mView != null) {
- mView.onDockedFirstAnimationFrame();
- }
- }
-
/** Called when top task docked. */
public void onDockedTopTask() {
if (mView != null) {
@@ -502,6 +495,9 @@
mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, true /* dismissOrMaximize */);
updateVisibility(false /* visible */);
mMinimized = false;
+ // Resets divider bar position to undefined, so new divider bar will apply default position
+ // next time entering split mode.
+ mDividerState.mRatioPositionBeforeMinimized = 0;
removeDivider();
mImePositionProcessor.reset();
}
diff --git a/libs/hwui/OWNERS b/libs/hwui/OWNERS
index c232d13..bb93e66 100644
--- a/libs/hwui/OWNERS
+++ b/libs/hwui/OWNERS
@@ -5,3 +5,6 @@
reed@google.com
scroggo@google.com
stani@google.com
+
+# For text, e.g. Typeface, Font, Minikin, etc.
+nona@google.com
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index cfba5d4..a690840 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -735,8 +735,7 @@
// ----------------------------------------------------------------------------
void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
- float y, float boundsLeft, float boundsTop, float boundsRight,
- float boundsBottom, float totalAdvance) {
+ float y, float totalAdvance) {
if (count <= 0 || paint.nothingToDraw()) return;
Paint paintCopy(paint);
if (mPaintFilter) {
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 1df2b26..2cb850c8 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -161,8 +161,7 @@
void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); }
virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
- float y, float boundsLeft, float boundsTop, float boundsRight,
- float boundsBottom, float totalAdvance) override;
+ float y, float totalAdvance) override;
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const Paint& paint, const SkPath& path, size_t start,
size_t end) override;
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 2a377bb..2001b56 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -84,13 +84,12 @@
class DrawTextFunctor {
public:
DrawTextFunctor(const minikin::Layout& layout, Canvas* canvas, const Paint& paint, float x,
- float y, minikin::MinikinRect& bounds, float totalAdvance)
+ float y, float totalAdvance)
: layout(layout)
, canvas(canvas)
, paint(paint)
, x(x)
, y(y)
- , bounds(bounds)
, totalAdvance(totalAdvance) {}
void operator()(size_t start, size_t end) {
@@ -114,19 +113,16 @@
Paint outlinePaint(paint);
simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, bounds.mLeft, bounds.mTop,
- bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance);
// inner
Paint innerPaint(paint);
simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
innerPaint.setStyle(SkPaint::kFill_Style);
- canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop,
- bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance);
} else {
// standard draw path
- canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop,
- bounds.mRight, bounds.mBottom, totalAdvance);
+ canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
}
}
@@ -136,7 +132,6 @@
const Paint& paint;
float x;
float y;
- minikin::MinikinRect& bounds;
float totalAdvance;
};
@@ -156,15 +151,12 @@
x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
- minikin::MinikinRect bounds;
- layout.getBounds(&bounds);
-
// Set align to left for drawing, as we don't want individual
// glyphs centered or right-aligned; the offset above takes
// care of all alignment.
paint.setTextAlign(Paint::kLeft_Align);
- DrawTextFunctor f(layout, this, paint, x, y, bounds, layout.getAdvance());
+ DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
}
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 333567b..817c7ee 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -288,8 +288,7 @@
* totalAdvance: used to define width of text decorations (underlines, strikethroughs).
*/
virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
- float y, float boundsLeft, float boundsTop, float boundsRight,
- float boundsBottom, float totalAdvance) = 0;
+ float y,float totalAdvance) = 0;
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const Paint& paint, const SkPath& path, size_t start,
size_t end) = 0;
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index a6137b0..0e338f3 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -33,8 +33,7 @@
MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize,
std::string_view filePath, int ttcIndex,
const std::vector<minikin::FontVariation>& axes)
- : minikin::MinikinFont(typeface->uniqueID())
- , mTypeface(std::move(typeface))
+ : mTypeface(std::move(typeface))
, mFontData(fontData)
, mFontSize(fontSize)
, mTtcIndex(ttcIndex)
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index 5f6b53a..b802908 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -21,6 +21,7 @@
#include <log/log.h>
#include <minikin/MeasuredText.h>
+#include <minikin/Measurement.h>
#include "Paint.h"
#include "SkPathMeasure.h"
#include "Typeface.h"
@@ -69,6 +70,18 @@
}
}
+void MinikinUtils::getBounds(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface,
+ const uint16_t* buf, size_t bufSize, minikin::MinikinRect* out) {
+ minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
+
+ const minikin::U16StringPiece textBuf(buf, bufSize);
+ const minikin::StartHyphenEdit startHyphen = paint->getStartHyphenEdit();
+ const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit();
+
+ minikin::getBounds(textBuf, minikin::Range(0, textBuf.size()), bidiFlags, minikinPaint,
+ startHyphen, endHyphen, out);
+}
+
float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf, size_t start,
size_t count, size_t bufSize, float* advances) {
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 0eacde9..6cde9c5 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -48,6 +48,9 @@
size_t contextStart, size_t contextCount,
minikin::MeasuredText* mt);
+ static void getBounds(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface,
+ const uint16_t* buf, size_t bufSize, minikin::MinikinRect* out);
+
static float measureText(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf,
size_t start, size_t count, size_t bufSize,
diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp
index ecbb55ec..77f46be 100644
--- a/libs/hwui/jni/Graphics.cpp
+++ b/libs/hwui/jni/Graphics.cpp
@@ -9,6 +9,7 @@
#include "GraphicsJNI.h"
#include "SkCanvas.h"
+#include "SkFontMetrics.h"
#include "SkMath.h"
#include "SkRegion.h"
#include <cutils/ashmem.h>
@@ -228,6 +229,20 @@
static jclass gTransferParameters_class;
static jmethodID gTransferParameters_constructorMethodID;
+static jclass gFontMetrics_class;
+static jfieldID gFontMetrics_top;
+static jfieldID gFontMetrics_ascent;
+static jfieldID gFontMetrics_descent;
+static jfieldID gFontMetrics_bottom;
+static jfieldID gFontMetrics_leading;
+
+static jclass gFontMetricsInt_class;
+static jfieldID gFontMetricsInt_top;
+static jfieldID gFontMetricsInt_ascent;
+static jfieldID gFontMetricsInt_descent;
+static jfieldID gFontMetricsInt_bottom;
+static jfieldID gFontMetricsInt_leading;
+
///////////////////////////////////////////////////////////////////////////////
void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
@@ -468,6 +483,32 @@
return r;
}
+void GraphicsJNI::set_metrics(JNIEnv* env, jobject metrics, const SkFontMetrics& skmetrics) {
+ if (metrics == nullptr) return;
+ SkASSERT(env->IsInstanceOf(metrics, gFontMetrics_class));
+ env->SetFloatField(metrics, gFontMetrics_top, SkScalarToFloat(skmetrics.fTop));
+ env->SetFloatField(metrics, gFontMetrics_ascent, SkScalarToFloat(skmetrics.fAscent));
+ env->SetFloatField(metrics, gFontMetrics_descent, SkScalarToFloat(skmetrics.fDescent));
+ env->SetFloatField(metrics, gFontMetrics_bottom, SkScalarToFloat(skmetrics.fBottom));
+ env->SetFloatField(metrics, gFontMetrics_leading, SkScalarToFloat(skmetrics.fLeading));
+}
+
+int GraphicsJNI::set_metrics_int(JNIEnv* env, jobject metrics, const SkFontMetrics& skmetrics) {
+ int ascent = SkScalarRoundToInt(skmetrics.fAscent);
+ int descent = SkScalarRoundToInt(skmetrics.fDescent);
+ int leading = SkScalarRoundToInt(skmetrics.fLeading);
+
+ if (metrics) {
+ SkASSERT(env->IsInstanceOf(metrics, gFontMetricsInt_class));
+ env->SetIntField(metrics, gFontMetricsInt_top, SkScalarFloorToInt(skmetrics.fTop));
+ env->SetIntField(metrics, gFontMetricsInt_ascent, ascent);
+ env->SetIntField(metrics, gFontMetricsInt_descent, descent);
+ env->SetIntField(metrics, gFontMetricsInt_bottom, SkScalarCeilToInt(skmetrics.fBottom));
+ env->SetIntField(metrics, gFontMetricsInt_leading, leading);
+ }
+ return descent - ascent + leading;
+}
+
///////////////////////////////////////////////////////////////////////////////////////////
jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, skia::BitmapRegionDecoder* bitmap)
@@ -764,5 +805,23 @@
gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class,
"<init>", "(DDDDDDD)V");
+ gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
+ gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
+
+ gFontMetrics_top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
+ gFontMetrics_ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
+ gFontMetrics_descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
+ gFontMetrics_bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
+ gFontMetrics_leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
+
+ gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
+ gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
+
+ gFontMetricsInt_top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
+ gFontMetricsInt_ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
+ gFontMetricsInt_descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
+ gFontMetricsInt_bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
+ gFontMetricsInt_leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
+
return 0;
}
diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h
index 79ab617..541d5a5 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -18,6 +18,7 @@
#include "graphics_jni_helpers.h"
class SkCanvas;
+struct SkFontMetrics;
namespace android {
namespace skia {
@@ -85,6 +86,17 @@
bool* isHardware);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
+ /**
+ * Set SkFontMetrics to Java Paint.FontMetrics.
+ * Do nothing if metrics is nullptr.
+ */
+ static void set_metrics(JNIEnv*, jobject metrics, const SkFontMetrics& skmetrics);
+ /**
+ * Set SkFontMetrics to Java Paint.FontMetricsInt and return recommended interline space.
+ * Do nothing if metrics is nullptr.
+ */
+ static int set_metrics_int(JNIEnv*, jobject metrics, const SkFontMetrics& skmetrics);
+
/*
* LegacyBitmapConfig is the old enum in Skia that matched the enum int values
* in Bitmap.Config. Skia no longer supports this config, but has replaced it
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index 554674a..f6c8496 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -59,20 +59,6 @@
namespace android {
-struct JMetricsID {
- jfieldID top;
- jfieldID ascent;
- jfieldID descent;
- jfieldID bottom;
- jfieldID leading;
-};
-
-static jclass gFontMetrics_class;
-static JMetricsID gFontMetrics_fieldID;
-
-static jclass gFontMetricsInt_class;
-static JMetricsID gFontMetricsInt_fieldID;
-
static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count,
const SkPoint pos[], SkPath* dst) {
dst->reset();
@@ -356,18 +342,13 @@
}
static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
- const Paint& paint, const Typeface* typeface, jint bidiFlags) {
+ const Paint& paint, const Typeface* typeface, jint bidiFlagsInt) {
SkRect r;
SkIRect ir;
- minikin::Layout layout = MinikinUtils::doLayout(&paint,
- static_cast<minikin::Bidi>(bidiFlags), typeface,
- text, count, // text buffer
- 0, count, // draw range
- 0, count, // context range
- nullptr);
minikin::MinikinRect rect;
- layout.getBounds(&rect);
+ minikin::Bidi bidiFlags = static_cast<minikin::Bidi>(bidiFlagsInt);
+ MinikinUtils::getBounds(&paint, bidiFlags, typeface, text, count, &rect);
r.fLeft = rect.mLeft;
r.fTop = rect.mTop;
r.fRight = rect.mRight;
@@ -618,35 +599,14 @@
static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
SkFontMetrics metrics;
SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
-
- if (metricsObj) {
- SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
- env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
- }
+ GraphicsJNI::set_metrics(env, metricsObj, metrics);
return SkScalarToFloat(spacing);
}
static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
SkFontMetrics metrics;
-
getMetricsInternal(paintHandle, &metrics);
- int ascent = SkScalarRoundToInt(metrics.fAscent);
- int descent = SkScalarRoundToInt(metrics.fDescent);
- int leading = SkScalarRoundToInt(metrics.fLeading);
-
- if (metricsObj) {
- SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
- env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
- }
- return descent - ascent + leading;
+ return GraphicsJNI::set_metrics_int(env, metricsObj, metrics);
}
@@ -1137,24 +1097,6 @@
};
int register_android_graphics_Paint(JNIEnv* env) {
- gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
- gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
-
- gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
- gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
- gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
- gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
- gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
-
- gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
- gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
-
- gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
- gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
- gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
- gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
- gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
-
return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
}
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 7cb7723..0a194f9 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -224,7 +224,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////
static jlong BlurShader_create(JNIEnv* env , jobject o, jlong matrixPtr, jfloat sigmaX,
- jfloat sigmaY, jlong shaderHandle) {
+ jfloat sigmaY, jlong shaderHandle, jint edgeTreatment) {
auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
auto* inputShader = reinterpret_cast<Shader*>(shaderHandle);
@@ -232,6 +232,7 @@
sigmaX,
sigmaY,
inputShader,
+ static_cast<SkTileMode>(edgeTreatment),
matrix
);
return reinterpret_cast<jlong>(blurShader);
@@ -291,7 +292,7 @@
};
static const JNINativeMethod gBlurShaderMethods[] = {
- { "nativeCreate", "(JFFJ)J", (void*)BlurShader_create }
+ { "nativeCreate", "(JFFJI)J", (void*)BlurShader_create }
};
static const JNINativeMethod gLinearGradientMethods[] = {
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 996cdce..bcdb4f5 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -18,6 +18,8 @@
#define LOG_TAG "Minikin"
#include "SkData.h"
+#include "SkFont.h"
+#include "SkFontMetrics.h"
#include "SkFontMgr.h"
#include "SkRefCnt.h"
#include "SkTypeface.h"
@@ -27,6 +29,7 @@
#include "FontUtils.h"
#include <hwui/MinikinSkia.h>
+#include <hwui/Paint.h>
#include <hwui/Typeface.h>
#include <minikin/FontFamily.h>
#include <ui/FatVector.h>
@@ -120,6 +123,36 @@
return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
}
+// Fast Native
+static jlong Font_Builder_clone(JNIEnv* env, jobject clazz, jlong fontPtr, jlong builderPtr,
+ jint weight, jboolean italic, jint ttcIndex) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+ std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
+
+ // Reconstruct SkTypeface with different arguments from existing SkTypeface.
+ FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
+ for (const auto& axis : builder->axes) {
+ skVariation.push_back({axis.axisTag, axis.value});
+ }
+ SkFontArguments args;
+ args.setCollectionIndex(ttcIndex);
+ args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
+
+ sk_sp<SkTypeface> newTypeface = minikinSkia->GetSkTypeface()->makeClone(args);
+
+ std::shared_ptr<minikin::MinikinFont> newMinikinFont = std::make_shared<MinikinFontSkia>(
+ std::move(newTypeface),
+ minikinSkia->GetFontData(),
+ minikinSkia->GetFontSize(),
+ minikinSkia->getFilePath(),
+ minikinSkia->GetFontIndex(),
+ builder->axes);
+ minikin::Font newFont = minikin::Font::Builder(newMinikinFont).setWeight(weight)
+ .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+ return reinterpret_cast<jlong>(new FontWrapper(std::move(newFont)));
+}
+
// Critical Native
static jlong Font_Builder_getReleaseNativeFont(CRITICAL_JNI_PARAMS) {
return reinterpret_cast<jlong>(releaseFont);
@@ -127,16 +160,64 @@
///////////////////////////////////////////////////////////////////////////////
+// Fast Native
+static jfloat Font_getGlyphBounds(JNIEnv* env, jobject, jlong fontHandle, jint glyphId,
+ jlong paintHandle, jobject rect) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+ SkFont* skFont = &paint->getSkFont();
+ // We don't use populateSkFont since it is designed to be used for layout result with addressing
+ // auto fake-bolding.
+ skFont->setTypeface(minikinSkia->RefSkTypeface());
+
+ uint16_t glyph16 = glyphId;
+ SkRect skBounds;
+ SkScalar skWidth;
+ skFont->getWidthsBounds(&glyph16, 1, &skWidth, &skBounds, nullptr);
+ GraphicsJNI::rect_to_jrectf(skBounds, env, rect);
+ return SkScalarToFloat(skWidth);
+}
+
+// Fast Native
+static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong paintHandle,
+ jobject metricsObj) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+ SkFont* skFont = &paint->getSkFont();
+ // We don't use populateSkFont since it is designed to be used for layout result with addressing
+ // auto fake-bolding.
+ skFont->setTypeface(minikinSkia->RefSkTypeface());
+
+ SkFontMetrics metrics;
+ SkScalar spacing = skFont->getMetrics(&metrics);
+ GraphicsJNI::set_metrics(env, metricsObj, metrics);
+ return spacing;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
static const JNINativeMethod gFontBuilderMethods[] = {
{ "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
{ "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
{ "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
+ { "nClone", "(JJIZI)J", (void*) Font_Builder_clone },
{ "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
};
+static const JNINativeMethod gFontMethods[] = {
+ { "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
+ { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
+};
+
int register_android_graphics_fonts_Font(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
- NELEM(gFontBuilderMethods));
+ NELEM(gFontBuilderMethods)) +
+ RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
+ NELEM(gFontMethods));
}
}
diff --git a/libs/hwui/shader/BlurShader.cpp b/libs/hwui/shader/BlurShader.cpp
index fa10be1..2abd871 100644
--- a/libs/hwui/shader/BlurShader.cpp
+++ b/libs/hwui/shader/BlurShader.cpp
@@ -20,13 +20,14 @@
#include "utils/Blur.h"
namespace android::uirenderer {
-BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix)
+BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, SkTileMode edgeTreatment,
+ const SkMatrix* matrix)
: Shader(matrix)
, skImageFilter(
SkImageFilters::Blur(
Blur::convertRadiusToSigma(radiusX),
Blur::convertRadiusToSigma(radiusY),
- SkTileMode::kClamp,
+ edgeTreatment,
inputShader ? inputShader->asSkImageFilter() : nullptr,
nullptr)
) { }
diff --git a/libs/hwui/shader/BlurShader.h b/libs/hwui/shader/BlurShader.h
index 9eb22bd..60a1589 100644
--- a/libs/hwui/shader/BlurShader.h
+++ b/libs/hwui/shader/BlurShader.h
@@ -30,8 +30,12 @@
*
* This will blur the contents of the provided input shader if it is non-null, otherwise
* the source bitmap will be blurred instead.
+ *
+ * The edge treatment parameter determines how content near the edges of the source is to
+ * participate in the blur
*/
- BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix);
+ BlurShader(float radiusX, float radiusY, Shader* inputShader, SkTileMode edgeTreatment,
+ const SkMatrix* matrix);
~BlurShader() override;
protected:
sk_sp<SkImageFilter> makeSkImageFilter() override;
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 1a09b1c..b5baafd 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -31,10 +31,12 @@
namespace {
-constexpr char kRobotoRegular[] = "/system/fonts/Roboto-Regular.ttf";
-constexpr char kRobotoBold[] = "/system/fonts/Roboto-Bold.ttf";
-constexpr char kRobotoItalic[] = "/system/fonts/Roboto-Italic.ttf";
-constexpr char kRobotoBoldItalic[] = "/system/fonts/Roboto-BoldItalic.ttf";
+constexpr char kRobotoVariable[] = "/system/fonts/Roboto-Regular.ttf";
+
+constexpr char kRegularFont[] = "/system/fonts/NotoSerif-Regular.ttf";
+constexpr char kBoldFont[] = "/system/fonts/NotoSerif-Bold.ttf";
+constexpr char kItalicFont[] = "/system/fonts/NotoSerif-Italic.ttf";
+constexpr char kBoldItalicFont[] = "/system/fonts/NotoSerif-BoldItalic.ttf";
void unmap(const void* ptr, void* context) {
void* p = const_cast<void*>(ptr);
@@ -68,7 +70,7 @@
TEST(TypefaceTest, resolveDefault_and_setDefaultTest) {
std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
- makeSingleFamlyVector(kRobotoRegular), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ makeSingleFamlyVector(kRobotoVariable), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(regular.get(), Typeface::resolveDefault(regular.get()));
// Keep the original to restore it later.
@@ -347,71 +349,71 @@
// In Java, new
// Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(false).build();
std::unique_ptr<Typeface> regular(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false));
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 400, false));
EXPECT_EQ(400, regular->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new
- // Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build();
+ // Typeface.Builder("Roboto-Regular.ttf").setWeight(700).setItalic(false).build();
std::unique_ptr<Typeface> bold(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false));
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 700, false));
EXPECT_EQ(700, bold->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new
- // Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build();
+ // Typeface.Builder("Roboto-Regular.ttf").setWeight(400).setItalic(true).build();
std::unique_ptr<Typeface> italic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true));
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 400, true));
EXPECT_EQ(400, italic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
// new
- // Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build();
+ // Typeface.Builder("Roboto-Regular.ttf").setWeight(700).setItalic(true).build();
std::unique_ptr<Typeface> boldItalic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true));
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 700, true));
EXPECT_EQ(700, boldItalic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
// new
- // Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build();
+ // Typeface.Builder("Roboto-Regular.ttf").setWeight(1100).setItalic(false).build();
std::unique_ptr<Typeface> over1000(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false));
+ Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoVariable), 1100, false));
EXPECT_EQ(1000, over1000->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, over1000->fStyle.slant());
EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
}
TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) {
- // In Java, new Typeface.Builder("Roboto-Regular.ttf").build();
+ // In Java, new Typeface.Builder("Family-Regular.ttf").build();
std::unique_ptr<Typeface> regular(Typeface::createFromFamilies(
- makeSingleFamlyVector(kRobotoRegular), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ makeSingleFamlyVector(kRegularFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(400, regular->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, regular->fStyle.slant());
EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
- // In Java, new Typeface.Builder("Roboto-Bold.ttf").build();
+ // In Java, new Typeface.Builder("Family-Bold.ttf").build();
std::unique_ptr<Typeface> bold(Typeface::createFromFamilies(
- makeSingleFamlyVector(kRobotoBold), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ makeSingleFamlyVector(kBoldFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(700, bold->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::UPRIGHT, bold->fStyle.slant());
EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
- // In Java, new Typeface.Builder("Roboto-Italic.ttf").build();
+ // In Java, new Typeface.Builder("Family-Italic.ttf").build();
std::unique_ptr<Typeface> italic(Typeface::createFromFamilies(
- makeSingleFamlyVector(kRobotoItalic), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ makeSingleFamlyVector(kItalicFont), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(400, italic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, italic->fStyle.slant());
EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
- // In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build();
+ // In Java, new Typeface.Builder("Family-BoldItalic.ttf").build();
std::unique_ptr<Typeface> boldItalic(
- Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic),
+ Typeface::createFromFamilies(makeSingleFamlyVector(kBoldItalicFont),
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(700, boldItalic->fStyle.weight());
EXPECT_EQ(minikin::FontStyle::Slant::ITALIC, boldItalic->fStyle.slant());
@@ -420,8 +422,8 @@
TEST(TypefaceTest, createFromFamilies_Family) {
std::vector<std::shared_ptr<minikin::FontFamily>> families = {
- buildFamily(kRobotoRegular), buildFamily(kRobotoBold), buildFamily(kRobotoItalic),
- buildFamily(kRobotoBoldItalic)};
+ buildFamily(kRegularFont), buildFamily(kBoldFont), buildFamily(kItalicFont),
+ buildFamily(kBoldItalicFont)};
std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(400, typeface->fStyle.weight());
@@ -430,7 +432,7 @@
TEST(TypefaceTest, createFromFamilies_Family_withoutRegular) {
std::vector<std::shared_ptr<minikin::FontFamily>> families = {
- buildFamily(kRobotoBold), buildFamily(kRobotoItalic), buildFamily(kRobotoBoldItalic)};
+ buildFamily(kBoldFont), buildFamily(kItalicFont), buildFamily(kBoldItalicFont)};
std::unique_ptr<Typeface> typeface(Typeface::createFromFamilies(
std::move(families), RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(700, typeface->fStyle.weight());
diff --git a/location/java/android/location/timezone/LocationTimeZoneEvent.java b/location/java/android/location/timezone/LocationTimeZoneEvent.java
index 55bc507..d3fd5c3 100644
--- a/location/java/android/location/timezone/LocationTimeZoneEvent.java
+++ b/location/java/android/location/timezone/LocationTimeZoneEvent.java
@@ -23,6 +23,8 @@
import android.os.Parcelable;
import android.os.UserHandle;
+import com.android.internal.util.Preconditions;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -37,7 +39,7 @@
@IntDef({ EVENT_TYPE_UNKNOWN, EVENT_TYPE_PERMANENT_FAILURE, EVENT_TYPE_SUCCESS,
EVENT_TYPE_UNCERTAIN })
- @interface EventType {}
+ public @interface EventType {}
/** Uninitialized value for {@link #mEventType} - must not be used for real events. */
private static final int EVENT_TYPE_UNKNOWN = 0;
@@ -49,8 +51,8 @@
public static final int EVENT_TYPE_PERMANENT_FAILURE = 1;
/**
- * Indicates a successful geolocation time zone detection event. {@link #mTimeZoneIds} will be
- * non-null but can legitimately be empty, e.g. for disputed areas, oceans.
+ * Indicates a successful geolocation time zone detection event. {@link #getTimeZoneIds()} will
+ * be non-null but can legitimately be empty, e.g. for disputed areas, oceans.
*/
public static final int EVENT_TYPE_SUCCESS = 2;
@@ -81,10 +83,7 @@
mTimeZoneIds = immutableList(timeZoneIds);
boolean emptyTimeZoneIdListExpected = eventType != EVENT_TYPE_SUCCESS;
- if (emptyTimeZoneIdListExpected && !timeZoneIds.isEmpty()) {
- throw new IllegalStateException(
- "timeZoneIds must only have values when eventType is success");
- }
+ Preconditions.checkState(!emptyTimeZoneIdListExpected || timeZoneIds.isEmpty());
mElapsedRealtimeNanos = elapsedRealtimeNanos;
}
@@ -102,9 +101,7 @@
*
* <p>This value can be reliably compared to {@link
* android.os.SystemClock#elapsedRealtimeNanos}, to calculate the age of a fix and to compare
- * {@link LocationTimeZoneEvent} fixes. This is reliable because elapsed real-time is guaranteed
- * monotonic for each system boot and continues to increment even when the system is in deep
- * sleep.
+ * {@link LocationTimeZoneEvent} instances.
*
* @return elapsed real-time of fix, in nanoseconds since system boot.
*/
diff --git a/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java b/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java
index 2a37ef8..5c9d290 100644
--- a/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java
+++ b/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java
@@ -30,7 +30,9 @@
public final class LocationTimeZoneProviderRequest implements Parcelable {
public static final LocationTimeZoneProviderRequest EMPTY_REQUEST =
- new LocationTimeZoneProviderRequest(false);
+ new LocationTimeZoneProviderRequest(
+ false /* reportLocationTimeZone */,
+ 0 /* initializationTimeoutMillis */);
public static final Creator<LocationTimeZoneProviderRequest> CREATOR =
new Creator<LocationTimeZoneProviderRequest>() {
@@ -45,17 +47,36 @@
}
};
- /** Location time zone reporting is requested (true) */
private final boolean mReportLocationTimeZone;
- private LocationTimeZoneProviderRequest(boolean reportLocationTimeZone) {
+ private final long mInitializationTimeoutMillis;
+
+ private LocationTimeZoneProviderRequest(
+ boolean reportLocationTimeZone, long initializationTimeoutMillis) {
mReportLocationTimeZone = reportLocationTimeZone;
+ mInitializationTimeoutMillis = initializationTimeoutMillis;
}
+ /**
+ * Returns {@code true} if the provider should report events related to the device's current
+ * time zone, {@code false} otherwise.
+ */
public boolean getReportLocationTimeZone() {
return mReportLocationTimeZone;
}
+ // TODO(b/152744911) - once there are a couple of implementations, decide whether this needs to
+ // be passed to the LocationTimeZoneProvider and remove if it is not useful.
+ /**
+ * Returns the maximum time that the provider is allowed to initialize before it is expected to
+ * send an event of any sort. Only valid when {@link #getReportLocationTimeZone()} is {@code
+ * true}. Failure to send an event in this time (with some fuzz) may be interpreted as if the
+ * provider is uncertain of the time zone, and/or it could lead to the provider being disabled.
+ */
+ public long getInitializationTimeoutMillis() {
+ return mInitializationTimeoutMillis;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -63,14 +84,15 @@
@Override
public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeInt(mReportLocationTimeZone ? 1 : 0);
+ parcel.writeBoolean(mReportLocationTimeZone);
+ parcel.writeLong(mInitializationTimeoutMillis);
}
static LocationTimeZoneProviderRequest createFromParcel(Parcel in) {
- ClassLoader classLoader = LocationTimeZoneProviderRequest.class.getClassLoader();
- return new Builder()
- .setReportLocationTimeZone(in.readInt() == 1)
- .build();
+ boolean reportLocationTimeZone = in.readBoolean();
+ long initializationTimeoutMillis = in.readLong();
+ return new LocationTimeZoneProviderRequest(
+ reportLocationTimeZone, initializationTimeoutMillis);
}
@Override
@@ -82,31 +104,28 @@
return false;
}
LocationTimeZoneProviderRequest that = (LocationTimeZoneProviderRequest) o;
- return mReportLocationTimeZone == that.mReportLocationTimeZone;
+ return mReportLocationTimeZone == that.mReportLocationTimeZone
+ && mInitializationTimeoutMillis == that.mInitializationTimeoutMillis;
}
@Override
public int hashCode() {
- return Objects.hash(mReportLocationTimeZone);
+ return Objects.hash(mReportLocationTimeZone, mInitializationTimeoutMillis);
}
@Override
public String toString() {
- StringBuilder s = new StringBuilder();
- s.append("TimeZoneProviderRequest[");
- if (mReportLocationTimeZone) {
- s.append("ON");
- } else {
- s.append("OFF");
- }
- s.append(']');
- return s.toString();
+ return "LocationTimeZoneProviderRequest{"
+ + "mReportLocationTimeZone=" + mReportLocationTimeZone
+ + ", mInitializationTimeoutMillis=" + mInitializationTimeoutMillis
+ + "}";
}
/** @hide */
public static final class Builder {
private boolean mReportLocationTimeZone;
+ private long mInitializationTimeoutMillis;
/**
* Sets the property that enables / disables the provider. This is set to {@code false} by
@@ -117,10 +136,20 @@
return this;
}
+ /**
+ * Sets the initialization timeout. See {@link
+ * LocationTimeZoneProviderRequest#getInitializationTimeoutMillis()} for details.
+ */
+ public Builder setInitializationTimeoutMillis(long timeoutMillis) {
+ mInitializationTimeoutMillis = timeoutMillis;
+ return this;
+ }
+
/** Builds the {@link LocationTimeZoneProviderRequest} instance. */
@NonNull
public LocationTimeZoneProviderRequest build() {
- return new LocationTimeZoneProviderRequest(this.mReportLocationTimeZone);
+ return new LocationTimeZoneProviderRequest(
+ mReportLocationTimeZone, mInitializationTimeoutMillis);
}
}
}
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index cd45e8e..c0188c0 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -20,5 +20,8 @@
libs: [
"androidx.annotation_annotation",
],
- api_packages: ["com.android.location.provider"],
+ api_packages: [
+ "com.android.location.provider",
+ "com.android.location.timezone.provider",
+ ],
}
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
new file mode 100644
index 0000000..3675574
--- /dev/null
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
@@ -0,0 +1,172 @@
+/*
+ * 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.location.timezone.provider;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.location.timezone.LocationTimeZoneEvent;
+import android.os.SystemClock;
+import android.os.UserHandle;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * An event from a {@link LocationTimeZoneProviderBase} sent while determining a device's time zone
+ * using its location.
+ *
+ * @hide
+ */
+public final class LocationTimeZoneEventUnbundled {
+
+ @IntDef({ EVENT_TYPE_PERMANENT_FAILURE, EVENT_TYPE_SUCCESS, EVENT_TYPE_UNCERTAIN })
+ @interface EventType {}
+
+ /**
+ * Indicates there was a permanent failure. This is not generally expected, and probably means a
+ * required backend service has been turned down, or the client is unreasonably old.
+ */
+ public static final int EVENT_TYPE_PERMANENT_FAILURE =
+ LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
+
+ /**
+ * Indicates a successful geolocation time zone detection event. {@link #getTimeZoneIds()} will
+ * be non-null but can legitimately be empty, e.g. for disputed areas, oceans.
+ */
+ public static final int EVENT_TYPE_SUCCESS = LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
+
+ /**
+ * Indicates the time zone is not known because of an expected runtime state or error, e.g. when
+ * the provider is unable to detect location, or there was a problem when resolving the location
+ * to a time zone.
+ */
+ public static final int EVENT_TYPE_UNCERTAIN = LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
+
+ @NonNull
+ private final LocationTimeZoneEvent mDelegate;
+
+ private LocationTimeZoneEventUnbundled(@NonNull LocationTimeZoneEvent delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /**
+ * Returns the event type.
+ */
+ @Nullable
+ public @EventType int getEventType() {
+ return mDelegate.getEventType();
+ }
+
+ /**
+ * Gets the time zone IDs of this event. Contains zero or more IDs for a successful lookup.
+ * The value is undefined for an unsuccessful lookup. See also {@link #getEventType()}.
+ */
+ @NonNull
+ public List<String> getTimeZoneIds() {
+ return mDelegate.getTimeZoneIds();
+ }
+
+ /**
+ * Returns the information from this as a {@link LocationTimeZoneEvent}.
+ * @hide
+ */
+ @NonNull
+ public LocationTimeZoneEvent getInternalLocationTimeZoneEvent() {
+ return mDelegate;
+ }
+
+ @Override
+ public String toString() {
+ return "LocationTimeZoneEventUnbundled{"
+ + "mDelegate=" + mDelegate
+ + '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ LocationTimeZoneEventUnbundled that = (LocationTimeZoneEventUnbundled) o;
+ return mDelegate.equals(that.mDelegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return mDelegate.hashCode();
+ }
+
+ /**
+ * A builder of {@link LocationTimeZoneEventUnbundled} instances.
+ *
+ * @hide
+ */
+ public static final class Builder {
+
+ private @EventType int mEventType;
+ private @NonNull List<String> mTimeZoneIds = Collections.emptyList();
+
+ /**
+ * Set the time zone ID of this event.
+ */
+ @NonNull
+ public Builder setEventType(@EventType int eventType) {
+ checkValidEventType(eventType);
+ mEventType = eventType;
+ return this;
+ }
+
+ /**
+ * Sets the time zone IDs of this event.
+ */
+ @NonNull
+ public Builder setTimeZoneIds(@NonNull List<String> timeZoneIds) {
+ mTimeZoneIds = Objects.requireNonNull(timeZoneIds);
+ return this;
+ }
+
+ /**
+ * Builds a {@link LocationTimeZoneEventUnbundled} instance.
+ */
+ @NonNull
+ public LocationTimeZoneEventUnbundled build() {
+ final int internalEventType = this.mEventType;
+ LocationTimeZoneEvent event = new LocationTimeZoneEvent.Builder()
+ .setUserHandle(UserHandle.of(ActivityManager.getCurrentUser()))
+ .setEventType(internalEventType)
+ .setTimeZoneIds(mTimeZoneIds)
+ .setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos())
+ .build();
+ return new LocationTimeZoneEventUnbundled(event);
+ }
+ }
+
+ private static int checkValidEventType(int eventType) {
+ if (eventType != EVENT_TYPE_SUCCESS
+ && eventType != EVENT_TYPE_UNCERTAIN
+ && eventType != EVENT_TYPE_PERMANENT_FAILURE) {
+ throw new IllegalStateException("eventType=" + eventType);
+ }
+ return eventType;
+ }
+}
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
index c533c20..9df7166 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
@@ -18,7 +18,6 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.location.timezone.LocationTimeZoneEvent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -30,12 +29,34 @@
import java.util.Objects;
/**
- * Base class for location time zone providers implemented as unbundled services.
+ * A base class for location time zone providers implemented as unbundled services.
*
- * TODO(b/152744911): Provide details of the expected service actions and threading.
+ * <p>Provider implementations are enabled / disabled via a call to {@link
+ * #onSetRequest(LocationTimeZoneProviderRequestUnbundled)}.
+ *
+ * <p>Once enabled, providers are expected to detect the time zone if possible, and report the
+ * result via {@link #reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled)} with a type of
+ * either {@link LocationTimeZoneEventUnbundled#EVENT_TYPE_UNCERTAIN} or {@link
+ * LocationTimeZoneEventUnbundled#EVENT_TYPE_SUCCESS}. Providers may also report that they have
+ * permanently failed by sending an event of type {@link
+ * LocationTimeZoneEventUnbundled#EVENT_TYPE_PERMANENT_FAILURE}. See the javadocs for each event
+ * type for details.
+ *
+ * <p>Providers are expected to issue their first event within {@link
+ * LocationTimeZoneProviderRequest#getInitializationTimeoutMillis()}.
+ *
+ * <p>Once disabled or have failed, providers are required to stop producing events.
+ *
+ * <p>Threading:
+ *
+ * <p>Calls to {@link #reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled)} can be made on
+ * on any thread, but may be processed asynchronously by the system server. Similarly, calls to
+ * {@link #onSetRequest(LocationTimeZoneProviderRequestUnbundled)} may occur on any thread.
*
* <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
* API stable.
+ *
+ * @hide
*/
public abstract class LocationTimeZoneProviderBase {
@@ -64,11 +85,11 @@
/**
* Reports a new location time zone event from this provider.
*/
- public void reportLocationTimeZoneEvent(LocationTimeZoneEvent locationTimeZoneEvent) {
+ protected void reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled event) {
ILocationTimeZoneProviderManager manager = mManager;
if (manager != null) {
try {
- manager.onLocationTimeZoneEvent(locationTimeZoneEvent);
+ manager.onLocationTimeZoneEvent(event.getInternalLocationTimeZoneEvent());
} catch (RemoteException | RuntimeException e) {
Log.w(mTag, e);
}
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
index e898bbf..ab50dc3 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
@@ -34,15 +34,30 @@
private final LocationTimeZoneProviderRequest mRequest;
+ /** @hide */
public LocationTimeZoneProviderRequestUnbundled(
@NonNull LocationTimeZoneProviderRequest request) {
mRequest = Objects.requireNonNull(request);
}
+ /**
+ * Returns {@code true} if the provider should report events related to the device's current
+ * time zone, {@code false} otherwise.
+ */
public boolean getReportLocationTimeZone() {
return mRequest.getReportLocationTimeZone();
}
+ /**
+ * Returns the maximum time that the provider is allowed to initialize before it is expected to
+ * send an event of any sort. Only valid when {@link #getReportLocationTimeZone()} is {@code
+ * true}. Failure to send an event in this time (with some fuzz) may be interpreted as if the
+ * provider is uncertain of the time zone, and/or it could lead to the provider being disabled.
+ */
+ public long getInitializationTimeoutMillis() {
+ return mRequest.getInitializationTimeoutMillis();
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
diff --git a/media/Android.bp b/media/Android.bp
index 8895b3a..828707b 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,85 +1,52 @@
aidl_interface {
name: "audio_common-aidl",
unstable: true,
- local_include_dir: "java",
+ local_include_dir: "aidl",
srcs: [
- "java/android/media/audio/common/AudioChannelMask.aidl",
- "java/android/media/audio/common/AudioConfig.aidl",
- "java/android/media/audio/common/AudioFormat.aidl",
- "java/android/media/audio/common/AudioOffloadInfo.aidl",
- "java/android/media/audio/common/AudioStreamType.aidl",
- "java/android/media/audio/common/AudioUsage.aidl",
+ "aidl/android/media/audio/common/AudioChannelMask.aidl",
+ "aidl/android/media/audio/common/AudioConfig.aidl",
+ "aidl/android/media/audio/common/AudioFormat.aidl",
+ "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+ "aidl/android/media/audio/common/AudioStreamType.aidl",
+ "aidl/android/media/audio/common/AudioUsage.aidl",
],
- backend:
- {
- cpp: {
- enabled: true,
- },
- java: {
- // Already generated as part of the entire media java library.
- enabled: false,
- },
- },
}
aidl_interface {
name: "media_permission-aidl",
unstable: true,
- local_include_dir: "java",
+ local_include_dir: "aidl",
srcs: [
- "java/android/media/permission/Identity.aidl",
+ "aidl/android/media/permission/Identity.aidl",
],
- backend:
- {
- cpp: {
- enabled: true,
- },
- java: {
- // Already generated as part of the entire media java library.
- enabled: false,
- },
- },
}
aidl_interface {
name: "soundtrigger_middleware-aidl",
unstable: true,
- local_include_dir: "java",
+ local_include_dir: "aidl",
srcs: [
- "java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
- "java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
- "java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
- "java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
- "java/android/media/soundtrigger_middleware/ModelParameter.aidl",
- "java/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
- "java/android/media/soundtrigger_middleware/Phrase.aidl",
- "java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
- "java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
- "java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
- "java/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
- "java/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
- "java/android/media/soundtrigger_middleware/RecognitionMode.aidl",
- "java/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
- "java/android/media/soundtrigger_middleware/SoundModel.aidl",
- "java/android/media/soundtrigger_middleware/SoundModelType.aidl",
- "java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
- "java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
- "java/android/media/soundtrigger_middleware/Status.aidl",
+ "aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl",
+ "aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl",
+ "aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl",
+ "aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl",
+ "aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl",
+ "aidl/android/media/soundtrigger_middleware/ModelParameter.aidl",
+ "aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl",
+ "aidl/android/media/soundtrigger_middleware/Phrase.aidl",
+ "aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl",
+ "aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl",
+ "aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl",
+ "aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl",
+ "aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl",
+ "aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl",
+ "aidl/android/media/soundtrigger_middleware/SoundModel.aidl",
+ "aidl/android/media/soundtrigger_middleware/SoundModelType.aidl",
+ "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl",
+ "aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl",
+ "aidl/android/media/soundtrigger_middleware/Status.aidl",
],
- backend:
- {
- cpp: {
- enabled: true,
- },
- java: {
- // Already generated as part of the entire media java library.
- enabled: false,
- },
- ndk: {
- // Not currently needed, and disabled because of b/146172425
- enabled: false,
- },
- },
imports: [
"audio_common-aidl",
"media_permission-aidl",
diff --git a/media/OWNERS b/media/OWNERS
index c95ac6c..36df3a0 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -9,6 +9,7 @@
hunga@google.com
insun@google.com
jaewan@google.com
+jinpark@google.com
jmtrivi@google.com
jsharkey@android.com
klhyun@google.com
@@ -17,6 +18,3 @@
philburk@google.com
sungsoo@google.com
wonsik@google.com
-
-# For maintaining sync with AndroidX code
-per-file ExifInterface.java = jinpark@google.com, sungsoo@google.com
diff --git a/media/java/android/media/audio/common/AudioChannelMask.aidl b/media/aidl/android/media/audio/common/AudioChannelMask.aidl
similarity index 100%
rename from media/java/android/media/audio/common/AudioChannelMask.aidl
rename to media/aidl/android/media/audio/common/AudioChannelMask.aidl
diff --git a/media/java/android/media/audio/common/AudioConfig.aidl b/media/aidl/android/media/audio/common/AudioConfig.aidl
similarity index 100%
rename from media/java/android/media/audio/common/AudioConfig.aidl
rename to media/aidl/android/media/audio/common/AudioConfig.aidl
diff --git a/media/java/android/media/audio/common/AudioFormat.aidl b/media/aidl/android/media/audio/common/AudioFormat.aidl
similarity index 100%
rename from media/java/android/media/audio/common/AudioFormat.aidl
rename to media/aidl/android/media/audio/common/AudioFormat.aidl
diff --git a/media/java/android/media/audio/common/AudioOffloadInfo.aidl b/media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
similarity index 100%
rename from media/java/android/media/audio/common/AudioOffloadInfo.aidl
rename to media/aidl/android/media/audio/common/AudioOffloadInfo.aidl
diff --git a/media/java/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
similarity index 100%
rename from media/java/android/media/audio/common/AudioStreamType.aidl
rename to media/aidl/android/media/audio/common/AudioStreamType.aidl
diff --git a/media/java/android/media/audio/common/AudioUsage.aidl b/media/aidl/android/media/audio/common/AudioUsage.aidl
similarity index 100%
rename from media/java/android/media/audio/common/AudioUsage.aidl
rename to media/aidl/android/media/audio/common/AudioUsage.aidl
diff --git a/media/java/android/media/permission/Identity.aidl b/media/aidl/android/media/permission/Identity.aidl
similarity index 100%
rename from media/java/android/media/permission/Identity.aidl
rename to media/aidl/android/media/permission/Identity.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl b/media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/AudioCapabilities.aidl
rename to media/aidl/android/media/soundtrigger_middleware/AudioCapabilities.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl b/media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
rename to media/aidl/android/media/soundtrigger_middleware/ConfidenceLevel.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
rename to media/aidl/android/media/soundtrigger_middleware/ISoundTriggerCallback.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
rename to media/aidl/android/media/soundtrigger_middleware/ISoundTriggerMiddlewareService.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
rename to media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ModelParameter.aidl b/media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/ModelParameter.aidl
rename to media/aidl/android/media/soundtrigger_middleware/ModelParameter.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl b/media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/ModelParameterRange.aidl
rename to media/aidl/android/media/soundtrigger_middleware/ModelParameterRange.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/OWNERS b/media/aidl/android/media/soundtrigger_middleware/OWNERS
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/OWNERS
rename to media/aidl/android/media/soundtrigger_middleware/OWNERS
diff --git a/media/java/android/media/soundtrigger_middleware/Phrase.aidl b/media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/Phrase.aidl
rename to media/aidl/android/media/soundtrigger_middleware/Phrase.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
rename to media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEvent.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
rename to media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionExtra.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
rename to media/aidl/android/media/soundtrigger_middleware/PhraseSoundModel.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/RecognitionConfig.aidl
rename to media/aidl/android/media/soundtrigger_middleware/RecognitionConfig.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/RecognitionEvent.aidl
rename to media/aidl/android/media/soundtrigger_middleware/RecognitionEvent.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/RecognitionMode.aidl
rename to media/aidl/android/media/soundtrigger_middleware/RecognitionMode.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/RecognitionStatus.aidl
rename to media/aidl/android/media/soundtrigger_middleware/RecognitionStatus.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
similarity index 94%
rename from media/java/android/media/soundtrigger_middleware/SoundModel.aidl
rename to media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
index 81d8291..cee3635 100644
--- a/media/java/android/media/soundtrigger_middleware/SoundModel.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/SoundModel.aidl
@@ -16,6 +16,7 @@
package android.media.soundtrigger_middleware;
import android.media.soundtrigger_middleware.SoundModelType;
+import android.os.ParcelFileDescriptor;
/**
* Base sound model descriptor. This struct can be extended for various specific types by way of
@@ -32,7 +33,7 @@
* was build for */
String vendorUuid;
/** Opaque data transparent to Android framework */
- FileDescriptor data;
+ ParcelFileDescriptor data;
/** Size of the above data, in bytes. */
int dataSize;
}
diff --git a/media/java/android/media/soundtrigger_middleware/SoundModelType.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/SoundModelType.aidl
rename to media/aidl/android/media/soundtrigger_middleware/SoundModelType.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
rename to media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleDescriptor.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl b/media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
rename to media/aidl/android/media/soundtrigger_middleware/SoundTriggerModuleProperties.aidl
diff --git a/media/java/android/media/soundtrigger_middleware/Status.aidl b/media/aidl/android/media/soundtrigger_middleware/Status.aidl
similarity index 100%
rename from media/java/android/media/soundtrigger_middleware/Status.aidl
rename to media/aidl/android/media/soundtrigger_middleware/Status.aidl
diff --git a/media/java/android/media/AudioAttributes.aidl b/media/java/android/media/AudioAttributes.aidl
index 04587f9..88a6f50 100644
--- a/media/java/android/media/AudioAttributes.aidl
+++ b/media/java/android/media/AudioAttributes.aidl
@@ -15,4 +15,4 @@
package android.media;
-parcelable AudioAttributes;
+@JavaOnlyStableParcelable parcelable AudioAttributes;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e1e55c2..22b5ca5 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -96,8 +96,8 @@
private Context mOriginalContext;
private Context mApplicationContext;
private long mVolumeKeyUpTime;
- private final boolean mUseVolumeKeySounds;
- private final boolean mUseFixedVolume;
+ private boolean mUseFixedVolumeInitialized;
+ private boolean mUseFixedVolume;
private static final String TAG = "AudioManager";
private static final boolean DEBUG = false;
private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
@@ -711,8 +711,6 @@
*/
@UnsupportedAppUsage
public AudioManager() {
- mUseVolumeKeySounds = true;
- mUseFixedVolume = false;
}
/**
@@ -721,10 +719,6 @@
@UnsupportedAppUsage
public AudioManager(Context context) {
setContext(context);
- mUseVolumeKeySounds = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_useVolumeKeySounds);
- mUseFixedVolume = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_useFixedVolume);
}
private Context getContext() {
@@ -823,6 +817,18 @@
* </ul>
*/
public boolean isVolumeFixed() {
+ synchronized (this) {
+ try {
+ if (!mUseFixedVolumeInitialized) {
+ mUseFixedVolume = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_useFixedVolume);
+ }
+ } catch (Exception e) {
+ } finally {
+ // only ever try once, so always consider initialized even if query failed
+ mUseFixedVolumeInitialized = true;
+ }
+ }
return mUseFixedVolume;
}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index ddc7db7..8845d69 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -31,7 +31,6 @@
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.os.Build;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -599,7 +598,6 @@
private static final int WEBP_CHUNK_TYPE_BYTE_LENGTH = 4;
private static final int WEBP_CHUNK_SIZE_BYTE_LENGTH = 4;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@GuardedBy("sFormatter")
private static SimpleDateFormat sFormatter;
@GuardedBy("sFormatterTz")
@@ -1446,18 +1444,17 @@
sExifPointerTagMap.put(EXIF_POINTER_TAGS[5].number, IFD_TYPE_ORF_IMAGE_PROCESSING); // 8256
}
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private String mFilename;
private FileDescriptor mSeekableFileDescriptor;
private AssetManager.AssetInputStream mAssetInputStream;
private boolean mIsInputStream;
private int mMimeType;
private boolean mIsExifDataOnly;
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(publicAlternatives = "Use {@link #getAttribute(java.lang.String)} "
+ + "instead.")
private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
private Set<Integer> mHandledIfdOffsets = new HashSet<>(EXIF_TAGS.length);
private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean mHasThumbnail;
private boolean mHasThumbnailStrips;
private boolean mAreThumbnailStripsConsecutive;
@@ -2409,11 +2406,8 @@
}
/**
- * Returns parsed {@code DateTime} value, or -1 if unavailable or invalid.
- *
- * @hide
+ * Returns parsed {@link #TAG_DATETIME} value, or -1 if unavailable or invalid.
*/
- @UnsupportedAppUsage
public @CurrentTimeMillisLong long getDateTime() {
return parseDateTime(getAttribute(TAG_DATETIME),
getAttribute(TAG_SUBSEC_TIME),
@@ -2421,10 +2415,7 @@
}
/**
- * Returns parsed {@code DateTimeDigitized} value, or -1 if unavailable or
- * invalid.
- *
- * @hide
+ * Returns parsed {@link #TAG_DATETIME_DIGITIZED} value, or -1 if unavailable or invalid.
*/
public @CurrentTimeMillisLong long getDateTimeDigitized() {
return parseDateTime(getAttribute(TAG_DATETIME_DIGITIZED),
@@ -2433,12 +2424,8 @@
}
/**
- * Returns parsed {@code DateTimeOriginal} value, or -1 if unavailable or
- * invalid.
- *
- * @hide
+ * Returns parsed {@link #TAG_DATETIME_ORIGINAL} value, or -1 if unavailable or invalid.
*/
- @UnsupportedAppUsage
public @CurrentTimeMillisLong long getDateTimeOriginal() {
return parseDateTime(getAttribute(TAG_DATETIME_ORIGINAL),
getAttribute(TAG_SUBSEC_TIME_ORIGINAL),
@@ -2490,9 +2477,7 @@
/**
* Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
* Returns -1 if the date time information if not available.
- * @hide
*/
- @UnsupportedAppUsage
public long getGpsDateTime() {
String date = getAttribute(TAG_GPS_DATESTAMP);
String time = getAttribute(TAG_GPS_TIMESTAMP);
@@ -2518,7 +2503,6 @@
}
/** {@hide} */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static float convertRationalLatLonToFloat(String rationalString, String ref) {
try {
String [] parts = rationalString.split(",");
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 755bbfb..1a49b85 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -20,10 +20,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.Iterator;
@@ -436,6 +438,52 @@
public static final String KEY_CAPTURE_RATE = "capture-rate";
/**
+ * A key for retrieving the slow-motion marker information associated with a video track.
+ * <p>
+ * The associated value is a ByteBuffer in {@link ByteOrder#BIG_ENDIAN}
+ * (networking order) of the following format:
+ * </p>
+ * <pre class="prettyprint">
+ * float(32) playbackRate;
+ * unsigned int(32) numMarkers;
+ * for (i = 0;i < numMarkers; i++) {
+ * int(64) timestampUs;
+ * float(32) speedRatio;
+ * }</pre>
+ * The meaning of each field is as follows:
+ * <table border="1" width="90%" align="center" cellpadding="5">
+ * <tbody>
+ * <tr>
+ * <td>playbackRate</td>
+ * <td>The frame rate at which the playback should happen (or the flattened
+ * clip should be).</td>
+ * </tr>
+ * <tr>
+ * <td>numMarkers</td>
+ * <td>The number of slow-motion markers that follows.</td>
+ * </tr>
+ * <tr>
+ * <td>timestampUs</td>
+ * <td>The starting point of a new segment.</td>
+ * </tr>
+ * <tr>
+ * <td>speedRatio</td>
+ * <td>The playback speed for that segment. The playback speed is a floating
+ * point number, indicating how fast the time progresses relative to that
+ * written in the container. (Eg. 4.0 means time goes 4x as fast, which
+ * makes 30fps become 120fps.)</td>
+ * </tr>
+ * </table>
+ * <p>
+ * The following constraints apply to the timestampUs of the markers:
+ * </p>
+ * <li>The timestampUs shall be monotonically increasing.</li>
+ * <li>The timestampUs shall fall within the time span of the video track.</li>
+ * <li>The first timestampUs should match that of the first video sample.</li>
+ */
+ public static final String KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
+
+ /**
* A key describing the frequency of key frames expressed in seconds between key frames.
* <p>
* This key is used by video encoders.
diff --git a/media/java/android/media/MediaMetadata.aidl b/media/java/android/media/MediaMetadata.aidl
index 66ee483..1d78da2a 100644
--- a/media/java/android/media/MediaMetadata.aidl
+++ b/media/java/android/media/MediaMetadata.aidl
@@ -15,4 +15,4 @@
package android.media;
-parcelable MediaMetadata;
+@JavaOnlyStableParcelable parcelable MediaMetadata;
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index e092411..21376bb 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -83,13 +83,13 @@
<pre class=prettyprint>
TranscodingRequest request =
- new TranscodingRequest.Builder()
- .setSourceUri(srcUri)
- .setDestinationUri(dstUri)
- .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
- .setPriority(REALTIME)
- .setVideoTrackFormat(videoFormat)
- .build();
+ new TranscodingRequest.Builder()
+ .setSourceUri(srcUri)
+ .setDestinationUri(dstUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(REALTIME)
+ .setVideoTrackFormat(videoFormat)
+ .build();
}</pre>
TODO(hkuang): Add architecture diagram showing the transcoding service and api.
@@ -498,6 +498,20 @@
/** Uri of the destination media file. */
private @NonNull Uri mDestinationUri;
+ /**
+ * The UID of the client that the TranscodingRequest is for. Only privileged caller could
+ * set this Uid as only they could do the transcoding on behalf of the client.
+ * -1 means not available.
+ */
+ private int mClientUid = -1;
+
+ /**
+ * The Pid of the client that the TranscodingRequest is for. Only privileged caller could
+ * set this Uid as only they could do the transcoding on behalf of the client.
+ * -1 means not available.
+ */
+ private int mClientPid = -1;
+
/** Type of the transcoding. */
private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
@@ -534,6 +548,8 @@
private TranscodingRequest(Builder b) {
mSourceUri = b.mSourceUri;
mDestinationUri = b.mDestinationUri;
+ mClientUid = b.mClientUid;
+ mClientPid = b.mClientPid;
mPriority = b.mPriority;
mType = b.mType;
mVideoTrackFormat = b.mVideoTrackFormat;
@@ -554,6 +570,16 @@
return mSourceUri;
}
+ /** Return the UID of the client that this request is for. -1 means not available. */
+ public int getClientUid() {
+ return mClientUid;
+ }
+
+ /** Return the PID of the client that this request is for. -1 means not available. */
+ public int getClientPid() {
+ return mClientPid;
+ }
+
/** Return destination uri of the transcoding. */
@NonNull
public Uri getDestinationUri() {
@@ -592,6 +618,8 @@
parcel.transcodingType = mType;
parcel.sourceFilePath = mSourceUri.toString();
parcel.destinationFilePath = mDestinationUri.toString();
+ parcel.clientUid = mClientUid;
+ parcel.clientPid = mClientPid;
parcel.requestedVideoTrackFormat = convertToVideoTrackFormat(mVideoTrackFormat);
if (mTestConfig != null) {
parcel.isForTesting = true;
@@ -667,6 +695,8 @@
public static final class Builder {
private @NonNull Uri mSourceUri;
private @NonNull Uri mDestinationUri;
+ private int mClientUid = -1;
+ private int mClientPid = -1;
private @TranscodingType int mType = TRANSCODING_TYPE_UNKNOWN;
private @TranscodingPriority int mPriority = PRIORITY_UNKNOWN;
private @Nullable MediaFormat mVideoTrackFormat;
@@ -710,6 +740,38 @@
}
/**
+ * Specify the UID of the client that this request is for.
+ * @param uid client Uid.
+ * @return The same builder instance.
+ * @throws IllegalArgumentException if uid is invalid.
+ * TODO(hkuang): Check the permission if it is allowed.
+ */
+ @NonNull
+ public Builder setClientUid(int uid) {
+ if (uid <= 0) {
+ throw new IllegalArgumentException("Invalid Uid");
+ }
+ mClientUid = uid;
+ return this;
+ }
+
+ /**
+ * Specify the PID of the client that this request is for.
+ * @param pid client Pid.
+ * @return The same builder instance.
+ * @throws IllegalArgumentException if pid is invalid.
+ * TODO(hkuang): Check the permission if it is allowed.
+ */
+ @NonNull
+ public Builder setClientPid(int pid) {
+ if (pid <= 0) {
+ throw new IllegalArgumentException("Invalid pid");
+ }
+ mClientPid = pid;
+ return this;
+ }
+
+ /**
* Specifies the priority of the transcoding.
*
* @param priority Must be one of the {@code PRIORITY_*}
@@ -1225,6 +1287,50 @@
}
}
+ @Override
+ public String toString() {
+ String result;
+ String status;
+
+ switch (mResult) {
+ case RESULT_NONE:
+ result = "RESULT_NONE";
+ break;
+ case RESULT_SUCCESS:
+ result = "RESULT_SUCCESS";
+ break;
+ case RESULT_ERROR:
+ result = "RESULT_ERROR";
+ break;
+ case RESULT_CANCELED:
+ result = "RESULT_CANCELED";
+ break;
+ default:
+ result = String.valueOf(mResult);
+ break;
+ }
+
+ switch (mStatus) {
+ case STATUS_PENDING:
+ status = "STATUS_PENDING";
+ break;
+ case STATUS_PAUSED:
+ status = "STATUS_PAUSED";
+ break;
+ case STATUS_RUNNING:
+ status = "STATUS_RUNNING";
+ break;
+ case STATUS_FINISHED:
+ status = "STATUS_FINISHED";
+ break;
+ default:
+ status = String.valueOf(mStatus);
+ break;
+ }
+ return String.format(" Job: {id: %d, status: %s, result: %s, progress: %d}",
+ mJobId, status, result, mProgress);
+ }
+
private void updateProgress(int newProgress) {
synchronized (mLock) {
mProgress = newProgress;
@@ -1275,6 +1381,8 @@
// Converts the request to TranscodingRequestParcel.
TranscodingRequestParcel requestParcel = transcodingRequest.writeToParcel();
+ Log.i(TAG, "Getting transcoding request " + transcodingRequest.getSourceUri());
+
// Submits the request to MediaTranscoding service.
try {
TranscodingJobParcel jobParcel = new TranscodingJobParcel();
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 9e48f1e..35cfaca 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -538,7 +538,7 @@
handler = new Handler(Looper.getMainLooper());
}
mSessionManager.addOnActiveSessionsChangedListener(mSessionListener, listenerComponent,
- UserHandle.myUserId(), handler);
+ handler);
mSessionListener.onActiveSessionsChanged(mSessionManager
.getActiveSessions(listenerComponent));
if (DEBUG) {
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 011e835..b662901 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -184,7 +184,7 @@
boolean bound = false;
try {
bound = mContext.bindService(intent, mServiceConnection,
- Context.BIND_AUTO_CREATE);
+ Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES);
} catch (Exception ex) {
Log.e(TAG, "Failed binding to service " + mServiceComponent);
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 9494295..3acb951 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
@@ -262,18 +263,14 @@
}
/**
- * Add a listener to be notified when the list of active sessions
- * changes.This requires the
- * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
- * the calling app. You may also retrieve this list if your app is an
- * enabled notification listener using the
- * {@link NotificationListenerService} APIs, in which case you must pass the
- * {@link ComponentName} of your enabled listener. Updates will be posted to
- * the thread that registered the listener.
+ * Add a listener to be notified when the list of active sessions changes. This requires the
+ * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be held by the calling
+ * app. You may also retrieve this list if your app is an enabled notification listener using
+ * the {@link NotificationListenerService} APIs, in which case you must pass the
+ * {@link ComponentName} of your enabled listener.
*
* @param sessionListener The listener to add.
- * @param notificationListener The enabled notification listener component.
- * May be null.
+ * @param notificationListener The enabled notification listener component. May be null.
*/
public void addOnActiveSessionsChangedListener(
@NonNull OnActiveSessionsChangedListener sessionListener,
@@ -282,18 +279,15 @@
}
/**
- * Add a listener to be notified when the list of active sessions
- * changes.This requires the
- * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
- * the calling app. You may also retrieve this list if your app is an
- * enabled notification listener using the
- * {@link NotificationListenerService} APIs, in which case you must pass the
- * {@link ComponentName} of your enabled listener. Updates will be posted to
- * the handler specified or to the caller's thread if the handler is null.
+ * Add a listener to be notified when the list of active sessions changes. This requires the
+ * {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be held by the calling
+ * app. You may also retrieve this list if your app is an enabled notification listener using
+ * the {@link NotificationListenerService} APIs, in which case you must pass the
+ * {@link ComponentName} of your enabled listener. Updates will be posted to the handler
+ * specified or to the caller's thread if the handler is null.
*
* @param sessionListener The listener to add.
- * @param notificationListener The enabled notification listener component.
- * May be null.
+ * @param notificationListener The enabled notification listener component. May be null.
* @param handler The handler to post events to.
*/
public void addOnActiveSessionsChangedListener(
@@ -304,21 +298,24 @@
}
/**
- * Add a listener to be notified when the list of active sessions
- * changes.This requires the
- * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
- * the calling app. You may also retrieve this list if your app is an
- * enabled notification listener using the
- * {@link NotificationListenerService} APIs, in which case you must pass the
- * {@link ComponentName} of your enabled listener.
+ * Add a listener to be notified when the list of active sessions changes for the given user.
+ * The calling app must have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+ * permission if it wants to call this method for a user that is not running the app.
+ * <p>
+ * This requires the {@link android.Manifest.permission#MEDIA_CONTENT_CONTROL} permission be
+ * held by the calling app. You may also retrieve this list if your app is an enabled
+ * notification listener using the {@link NotificationListenerService} APIs, in which case you
+ * must pass the {@link ComponentName} of your enabled listener. Updates will be posted to the
+ * handler specified or to the caller's thread if the handler is null.
*
* @param sessionListener The listener to add.
- * @param notificationListener The enabled notification listener component.
- * May be null.
+ * @param notificationListener The enabled notification listener component. May be null.
* @param userId The userId to listen for changes on.
* @param handler The handler to post updates on.
* @hide
*/
+ @SuppressLint({"ExecutorRegistration", "SamShouldBeLast"})
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void addOnActiveSessionsChangedListener(
@NonNull OnActiveSessionsChangedListener sessionListener,
@Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) {
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 33d6d64..21ed840 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -203,6 +203,42 @@
}
/**
+ * Verify that setting invalid pid will throw exception.
+ */
+ @Test
+ public void testCreateTranscodingWithInvalidClientPid() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(mSourceHEVCVideoUri)
+ .setDestinationUri(mDestinationUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setClientPid(-1)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ });
+ }
+
+ /**
+ * Verify that setting invalid uid will throw exception.
+ */
+ @Test
+ public void testCreateTranscodingWithInvalidClientUid() throws Exception {
+ assertThrows(IllegalArgumentException.class, () -> {
+ TranscodingRequest request =
+ new TranscodingRequest.Builder()
+ .setSourceUri(mSourceHEVCVideoUri)
+ .setDestinationUri(mDestinationUri)
+ .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+ .setClientUid(-1)
+ .setVideoTrackFormat(createMediaFormat())
+ .build();
+ });
+ }
+
+ /**
* Verify that setting null source uri will throw exception.
*/
@Test
@@ -425,16 +461,24 @@
MediaFormat videoTrackFormat = resolver.resolveVideoFormat();
assertNotNull(videoTrackFormat);
+ int pid = android.os.Process.myPid();
+ int uid = android.os.Process.myUid();
+
TranscodingRequest request =
new TranscodingRequest.Builder()
.setSourceUri(mSourceHEVCVideoUri)
.setDestinationUri(destinationUri)
.setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+ .setClientPid(pid)
+ .setClientUid(uid)
.setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
.setVideoTrackFormat(videoTrackFormat)
.build();
Executor listenerExecutor = Executors.newSingleThreadExecutor();
+ assertEquals(pid, request.getClientPid());
+ assertEquals(uid, request.getClientUid());
+
Log.i(TAG, "transcoding to " + videoTrackFormat);
TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
diff --git a/non-updatable-api/Android.bp b/non-updatable-api/Android.bp
new file mode 100644
index 0000000..4037781
--- /dev/null
+++ b/non-updatable-api/Android.bp
@@ -0,0 +1,35 @@
+// 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 {
+ default_visibility: ["//visibility:private"],
+}
+
+filegroup {
+ name: "non-updatable-current.txt",
+ srcs: ["current.txt"],
+ visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
+ name: "non-updatable-system-current.txt",
+ srcs: ["system-current.txt"],
+ visibility: ["//frameworks/base/api"],
+}
+
+filegroup {
+ name: "non-updatable-module-lib-current.txt",
+ srcs: ["module-lib-current.txt"],
+ visibility: ["//frameworks/base/api"],
+}
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 8030061..d46f1d1 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -14292,6 +14292,7 @@
public final class BlurShader extends android.graphics.Shader {
ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+ ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode);
}
public class Camera {
@@ -15177,6 +15178,20 @@
ctor public PaintFlagsDrawFilter(int, int);
}
+ public final class ParcelableColorSpace extends android.graphics.ColorSpace implements android.os.Parcelable {
+ ctor public ParcelableColorSpace(@NonNull android.graphics.ColorSpace);
+ method public int describeContents();
+ method @NonNull public float[] fromXyz(@NonNull float[]);
+ method @NonNull public android.graphics.ColorSpace getColorSpace();
+ method public float getMaxValue(int);
+ method public float getMinValue(int);
+ method public static boolean isParcelable(@NonNull android.graphics.ColorSpace);
+ method public boolean isWideGamut();
+ method @NonNull public float[] toXyz(@NonNull float[]);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.graphics.ParcelableColorSpace> CREATOR;
+ }
+
public class Path {
ctor public Path();
ctor public Path(@Nullable android.graphics.Path);
@@ -15612,6 +15627,7 @@
public enum Shader.TileMode {
enum_constant public static final android.graphics.Shader.TileMode CLAMP;
+ enum_constant public static final android.graphics.Shader.TileMode DECAL;
enum_constant public static final android.graphics.Shader.TileMode MIRROR;
enum_constant public static final android.graphics.Shader.TileMode REPEAT;
}
@@ -16355,7 +16371,9 @@
method @Nullable public android.graphics.fonts.FontVariationAxis[] getAxes();
method @NonNull public java.nio.ByteBuffer getBuffer();
method @Nullable public java.io.File getFile();
+ method public float getGlyphBounds(@IntRange(from=0) int, @NonNull android.graphics.Paint, @Nullable android.graphics.RectF);
method @NonNull public android.os.LocaleList getLocaleList();
+ method public void getMetrics(@NonNull android.graphics.Paint, @Nullable android.graphics.Paint.FontMetrics);
method @NonNull public android.graphics.fonts.FontStyle getStyle();
method @IntRange(from=0) public int getTtcIndex();
}
@@ -16367,6 +16385,7 @@
ctor public Font.Builder(@NonNull android.os.ParcelFileDescriptor, @IntRange(from=0) long, @IntRange(from=0xffffffff) long);
ctor public Font.Builder(@NonNull android.content.res.AssetManager, @NonNull String);
ctor public Font.Builder(@NonNull android.content.res.Resources, int);
+ ctor public Font.Builder(@NonNull android.graphics.fonts.Font);
method @NonNull public android.graphics.fonts.Font build() throws java.io.IOException;
method @NonNull public android.graphics.fonts.Font.Builder setFontVariationSettings(@Nullable String);
method @NonNull public android.graphics.fonts.Font.Builder setFontVariationSettings(@Nullable android.graphics.fonts.FontVariationAxis[]);
@@ -24895,6 +24914,10 @@
method public double getAttributeDouble(@NonNull String, double);
method public int getAttributeInt(@NonNull String, int);
method @Nullable public long[] getAttributeRange(@NonNull String);
+ method public long getDateTime();
+ method public long getDateTimeDigitized();
+ method public long getDateTimeOriginal();
+ method public long getGpsDateTime();
method public boolean getLatLong(float[]);
method public byte[] getThumbnail();
method public android.graphics.Bitmap getThumbnailBitmap();
@@ -26241,6 +26264,7 @@
field public static final String KEY_ROTATION = "rotation-degrees";
field public static final String KEY_SAMPLE_RATE = "sample-rate";
field public static final String KEY_SLICE_HEIGHT = "slice-height";
+ field public static final String KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
field public static final String KEY_STRIDE = "stride";
field public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
field public static final String KEY_TILE_HEIGHT = "tile-height";
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 3810325..6c0dd33 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -11,7 +11,7 @@
}
public class StatusBarManager {
- method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSimNetworkLock(boolean);
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
}
@@ -66,6 +66,7 @@
}
public final class MediaSessionManager {
+ method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler);
method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent);
method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int);
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 10fe058..e27ca09 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -4339,6 +4339,8 @@
}
public static final class MediaTranscodeManager.TranscodingRequest {
+ method public int getClientPid();
+ method public int getClientUid();
method @NonNull public android.net.Uri getDestinationUri();
method public int getPriority();
method @NonNull public android.net.Uri getSourceUri();
@@ -4349,6 +4351,8 @@
public static final class MediaTranscodeManager.TranscodingRequest.Builder {
ctor public MediaTranscodeManager.TranscodingRequest.Builder();
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build();
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int);
+ method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int);
method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri);
diff --git a/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml b/packages/CarSystemUI/res/drawable/system_bar_background_pill.xml
similarity index 69%
rename from packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml
rename to packages/CarSystemUI/res/drawable/system_bar_background_pill.xml
index 9b48a70..1b12eb4 100644
--- a/packages/SystemUI/res/drawable/tv_rect_dark_left_rounded.xml
+++ b/packages/CarSystemUI/res/drawable/system_bar_background_pill.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="UTF-8" ?>
<!--
- ~ Copyright (C) 2019 The Android Open Source Project
+ ~ Copyright (C) 2020 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -16,11 +16,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <corners
- android:bottomLeftRadius="8dp"
- android:topLeftRadius="8dp" />
- <solid android:color="@color/tv_audio_recording_indicator_background" />
-
+ android:shape="rectangle">
+ <solid android:color="@color/system_bar_background_pill_color"/>
+ <corners android:radius="30dp"/>
</shape>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index b07dde5..8314ba5 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -30,22 +30,13 @@
android:layout_height="wrap_content"
android:layoutDirection="ltr">
- <com.android.systemui.car.navigationbar.CarNavigationButton
+ <com.android.systemui.car.hvac.AdjustableTemperatureView
+ android:id="@+id/driver_hvac"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_alignParentStart="true"
- android:background="@null"
- systemui:broadcast="true"
- systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end">
-
- <com.android.systemui.car.hvac.AdjustableTemperatureView
- android:id="@+id/driver_hvac"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- systemui:hvacAreaId="49"
- systemui:hvacTempFormat="%.0f\u00B0" />
- </com.android.systemui.car.navigationbar.CarNavigationButton>
+ android:gravity="center_vertical"
+ systemui:hvacAreaId="49"
+ systemui:hvacTempFormat="%.0f\u00B0" />
<LinearLayout
android:layout_width="wrap_content"
@@ -66,69 +57,55 @@
android:id="@+id/home"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+ systemui:highlightWhenSelected="true"
systemui:icon="@drawable/car_ic_home"
- systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
- systemui:selectedIcon="@drawable/car_ic_home_selected"/>
+ systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"/>
<com.android.systemui.car.navigationbar.CarNavigationButton
android:id="@+id/phone_nav"
style="@style/NavigationBarButton"
+ systemui:highlightWhenSelected="true"
systemui:icon="@drawable/car_ic_phone"
systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
- systemui:packages="com.android.car.dialer"
- systemui:selectedIcon="@drawable/car_ic_phone_selected"/>
+ systemui:packages="com.android.car.dialer"/>
<com.android.systemui.car.navigationbar.CarNavigationButton
android:id="@+id/grid_nav"
style="@style/NavigationBarButton"
systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+ systemui:highlightWhenSelected="true"
systemui:icon="@drawable/car_ic_apps"
- systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
- systemui:selectedIcon="@drawable/car_ic_apps_selected"/>
+ systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"/>
<com.android.systemui.car.navigationbar.CarNavigationButton
android:id="@+id/hvac"
style="@style/NavigationBarButton"
+ systemui:highlightWhenSelected="true"
systemui:icon="@drawable/car_ic_hvac"
systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
- systemui:selectedIcon="@drawable/car_ic_hvac_selected"
systemui:broadcast="true"/>
<com.android.systemui.car.navigationbar.CarNavigationButton
android:id="@+id/notifications"
style="@style/NavigationBarButton"
+ systemui:highlightWhenSelected="true"
systemui:icon="@drawable/car_ic_notification"
systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"/>
- <com.android.systemui.car.navigationbar.AssitantButton
- android:id="@+id/assist"
- style="@style/NavigationBarButton"
- systemui:icon="@drawable/ic_mic_white"
- systemui:useDefaultAppIconForRole="true"/>
-
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
- <com.android.systemui.car.navigationbar.CarNavigationButton
+ <com.android.systemui.car.hvac.AdjustableTemperatureView
+ android:id="@+id/passenger_hvac"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
- android:background="@null"
- systemui:broadcast="true"
- systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end">
-
- <com.android.systemui.car.hvac.AdjustableTemperatureView
- android:id="@+id/passenger_hvac"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentEnd="true"
- android:gravity="center_vertical"
- systemui:hvacAreaId="68"
- systemui:hvacTempFormat="%.0f\u00B0" />
- </com.android.systemui.car.navigationbar.CarNavigationButton>
+ android:gravity="center_vertical"
+ systemui:hvacAreaId="68"
+ systemui:hvacTempFormat="%.0f\u00B0" />
</RelativeLayout>
<LinearLayout
diff --git a/packages/CarSystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
index a8f1157..9f79023 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -36,7 +36,7 @@
android:background="@android:color/transparent"
android:scaleType="fitCenter"
android:tintMode="src_in"
- android:tint="@color/car_nav_icon_fill_color"
+ android:tint="@color/car_nav_icon_fill_color_selected"
android:clickable="false"
/>
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index af8482a8..07c11c7 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -31,43 +31,30 @@
android:layoutDirection="ltr">
<FrameLayout
- android:id="@+id/user_name_container"
+ android:id="@+id/system_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
- android:layout_toStartOf="@+id/clock_container"
+ android:layout_marginTop="@dimen/car_padding_2"
+ android:layout_centerVertical="true"
+ android:gravity="center_vertical"
>
-
<com.android.systemui.car.navigationbar.CarNavigationButton
- android:id="@+id/user_name"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- systemui:icon="@null"
- systemui:intent="intent:#Intent;component=com.android.car.settings/.users.UserSwitcherActivity;launchFlags=0x24000000;end"
- >
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
+ android:background="@drawable/system_bar_background_pill"
+ android:layout_weight="1"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:gravity="center_vertical"
+ systemui:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$QuickSettingActivity;launchFlags=0x24000000;end">
+
+ <include
+ layout="@layout/system_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
android:gravity="center_vertical"
- >
- <ImageView
- android:id="@+id/user_avatar"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:src="@drawable/car_ic_user_icon"
- android:paddingLeft="@dimen/system_bar_user_icon_padding"
- android:paddingRight="@dimen/system_bar_user_icon_padding"
- />
- <TextView
- android:id="@+id/user_name_text"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:textAppearance="@style/TextAppearance.SystemBar.Username"
- android:maxLines="1"
- />
- </LinearLayout>
+ />
</com.android.systemui.car.navigationbar.CarNavigationButton>
</FrameLayout>
@@ -96,25 +83,51 @@
/>
</FrameLayout>
- <LinearLayout
- android:id="@+id/system_icon_area"
+ <FrameLayout
+ android:id="@+id/user_name_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
- android:paddingEnd="@*android:dimen/car_padding_1"
- android:gravity="center_vertical"
- android:orientation="horizontal"
+ android:layout_marginTop="@dimen/car_padding_2"
>
-
- <include
- layout="@layout/system_icons"
+ <com.android.systemui.car.navigationbar.CarNavigationButton
+ android:id="@+id/user_name"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/car_padding_2"
+ android:background="@drawable/system_bar_background_pill"
android:gravity="center_vertical"
- />
- </LinearLayout>
+ systemui:intent="intent:#Intent;component=com.android.car.settings/.users.UserSwitcherActivity;launchFlags=0x24000000;end"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:layout_marginEnd="@dimen/car_padding_2"
+ android:gravity="center_vertical"
+ >
+ <ImageView
+ android:id="@+id/user_avatar"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:src="@drawable/car_ic_user_icon"
+ android:layout_marginEnd="@dimen/system_bar_user_icon_padding"
+ />
+ <TextView
+ android:id="@+id/user_name_text"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:textAppearance="@style/TextAppearance.SystemBar.Username"
+ android:maxLines="1"
+ android:maxLength="10"
+ android:layout_marginEnd="@dimen/system_bar_user_icon_padding"
+ />
+ </LinearLayout>
+ </com.android.systemui.car.navigationbar.CarNavigationButton>
+ </FrameLayout>
</RelativeLayout>
</com.android.systemui.car.navigationbar.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/system_icons.xml b/packages/CarSystemUI/res/layout/system_icons.xml
index d235792..5c06075 100644
--- a/packages/CarSystemUI/res/layout/system_icons.xml
+++ b/packages/CarSystemUI/res/layout/system_icons.xml
@@ -24,10 +24,11 @@
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
- android:paddingEnd="4dp"
+ android:padding="10dp"
+ android:scaleType="fitCenter"
android:gravity="center_vertical"
android:orientation="horizontal"
/>
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2c97889..e7295aa 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -25,7 +25,8 @@
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout="@layout/notification_panel_container"/>
+ android:layout="@layout/notification_panel_container"
+ android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/>
<ViewStub android:id="@+id/keyguard_stub"
android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index c390cc8..6fe5004 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -32,8 +32,9 @@
<color name="system_bar_background_opaque">#ff172026</color>
<!-- colors for status bar -->
- <color name="system_bar_user_icon_color">#ffffff</color>
- <color name="system_bar_text_color">#ffffff</color>
+ <color name="system_bar_background_pill_color">#282A2D</color>
+ <color name="system_bar_user_icon_color">#FFFFFF</color>
+ <color name="system_bar_text_color">#FFFFFF</color>
<color name="status_bar_background_color">#33000000</color>
<drawable name="system_bar_background">@color/status_bar_background_color</drawable>
diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml
index 28b8ead..fe060ac 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -207,6 +207,12 @@
<dimen name="car_navigation_bar_width">760dp</dimen>
<dimen name="car_left_navigation_bar_width">96dp</dimen>
<dimen name="car_right_navigation_bar_width">96dp</dimen>
+ <!-- In order to change the height of the bottom nav bar, overlay navigation_bar_height in
+ frameworks/base/core/res/res instead. -->
+ <dimen name="car_bottom_navigation_bar_height">@*android:dimen/navigation_bar_height</dimen>
+ <!-- In order to change the height of the top nav bar, overlay status_bar_height in
+ frameworks/base/core/res/res instead. -->
+ <dimen name="car_top_navigation_bar_height">@*android:dimen/status_bar_height</dimen>
<dimen name="car_user_switcher_container_height">420dp</dimen>
<!-- This must be the negative of car_user_switcher_container_height for the animation. -->
diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml
index fbdb516..06ae7cf 100644
--- a/packages/CarSystemUI/res/values/strings.xml
+++ b/packages/CarSystemUI/res/values/strings.xml
@@ -22,6 +22,8 @@
<string name="hvac_min_text">Min</string>
<!-- String to represent largest setting of an HVAC system [CHAR LIMIT=10]-->
<string name="hvac_max_text">Max</string>
+ <!-- String to display when no HVAC temperature is available -->
+ <string name="hvac_null_temp_text" translatable="false">--</string>
<!-- Text for voice recognition toast. [CHAR LIMIT=60] -->
<string name="voice_recognition_toast">Voice recognition now handled by connected Bluetooth device</string>
<!-- Name of Guest Profile. [CHAR LIMIT=35] -->
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java
index 85d4ceb..af2a1d3 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/hvac/AdjustableTemperatureView.java
@@ -40,6 +40,9 @@
private float mMinTempC;
private float mMaxTempC;
private String mTempFormat;
+ private String mNullTempText;
+ private String mMinTempText;
+ private String mMaxTempText;
private boolean mDisplayInFahrenheit = false;
private HvacController mHvacController;
@@ -59,6 +62,9 @@
mTempFormat = getResources().getString(R.string.hvac_temperature_format);
mMinTempC = getResources().getFloat(R.dimen.hvac_min_value_celsius);
mMaxTempC = getResources().getFloat(R.dimen.hvac_max_value_celsius);
+ mNullTempText = getResources().getString(R.string.hvac_null_temp_text);
+ mMinTempText = getResources().getString(R.string.hvac_min_text);
+ mMaxTempText = getResources().getString(R.string.hvac_max_text);
initializeButtons();
}
@@ -69,12 +75,23 @@
@Override
public void setTemp(float tempC) {
- if (tempC > mMaxTempC || tempC < mMinTempC) {
- return;
- }
if (mTempTextView == null) {
mTempTextView = findViewById(R.id.hvac_temperature_text);
}
+ if (Float.isNaN(tempC)) {
+ mTempTextView.setText(mNullTempText);
+ return;
+ }
+ if (tempC <= mMinTempC) {
+ mTempTextView.setText(mMinTempText);
+ mCurrentTempC = mMinTempC;
+ return;
+ }
+ if (tempC >= mMaxTempC) {
+ mTempTextView.setText(mMaxTempText);
+ mCurrentTempC = mMaxTempC;
+ return;
+ }
mTempTextView.setText(String.format(mTempFormat,
mDisplayInFahrenheit ? convertToFahrenheit(tempC) : tempC));
mCurrentTempC = tempC;
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 e7e33a54..d2b931b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/CarNavigationButton.java
@@ -96,11 +96,11 @@
public void setSelected(boolean selected) {
super.setSelected(selected);
mSelected = selected;
+
if (mHighlightWhenSelected) {
- // Always apply selected alpha if the button does not toggle alpha based on selection
- // state.
- setAlpha(!mHighlightWhenSelected || mSelected ? mSelectedAlpha : mUnselectedAlpha);
+ setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
}
+
if (mShowMoreWhenSelected && mMoreIcon != null) {
mMoreIcon.setVisibility(selected ? VISIBLE : GONE);
}
@@ -299,10 +299,10 @@
mIsDefaultAppIconForRoleEnabled = typedArray.getBoolean(
R.styleable.CarNavigationButton_useDefaultAppIconForRole, false);
mIcon = findViewById(R.id.car_nav_button_icon_image);
- // Always apply selected alpha if the button does not toggle alpha based on selection state.
- mIcon.setAlpha(mHighlightWhenSelected ? mUnselectedAlpha : mSelectedAlpha);
+ // Always apply un-selected alpha regardless of if the button toggles alpha based on
+ // selection state.
+ setAlpha(mHighlightWhenSelected ? mUnselectedAlpha : mSelectedAlpha);
mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
- mMoreIcon.setAlpha(mSelectedAlpha);
mUnseenIcon = findViewById(R.id.car_nav_button_unseen_icon);
updateImage();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
index 078196e..92cf600 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
@@ -97,10 +97,12 @@
populateMaps();
readConfigs();
+
checkEnabledBarsHaveUniqueBarTypes();
checkAllOverlappingBarsHaveDifferentZOrders();
checkSystemBarEnabledForNotificationPanel();
checkHideBottomBarForKeyboardConfigSync();
+
setInsetPaddingsForOverlappingCorners();
sortSystemBarSidesByZOrder();
}
@@ -199,10 +201,10 @@
BAR_TITLE_MAP.put(LEFT, "LeftCarSystemBar");
BAR_TITLE_MAP.put(RIGHT, "RightCarSystemBar");
- BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_GESTURES);
- BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_GESTURES);
- BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_GESTURES);
- BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_GESTURES);
+ BAR_GESTURE_MAP.put(TOP, InsetsState.ITYPE_TOP_MANDATORY_GESTURES);
+ BAR_GESTURE_MAP.put(BOTTOM, InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES);
+ BAR_GESTURE_MAP.put(LEFT, InsetsState.ITYPE_LEFT_MANDATORY_GESTURES);
+ BAR_GESTURE_MAP.put(RIGHT, InsetsState.ITYPE_RIGHT_MANDATORY_GESTURES);
}
private void readConfigs() {
@@ -216,7 +218,7 @@
new SystemBarConfigBuilder()
.setSide(TOP)
.setGirth(mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height))
+ R.dimen.car_top_navigation_bar_height))
.setBarType(mResources.getInteger(R.integer.config_topSystemBarType))
.setZOrder(mResources.getInteger(R.integer.config_topSystemBarZOrder))
.setHideForKeyboard(mResources.getBoolean(
@@ -230,7 +232,7 @@
new SystemBarConfigBuilder()
.setSide(BOTTOM)
.setGirth(mResources.getDimensionPixelSize(
- com.android.internal.R.dimen.navigation_bar_height))
+ R.dimen.car_bottom_navigation_bar_height))
.setBarType(mResources.getInteger(R.integer.config_bottomSystemBarType))
.setZOrder(
mResources.getInteger(R.integer.config_bottomSystemBarZOrder))
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 c7155f4..b647f13 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,8 +16,6 @@
package com.android.systemui.car.notification;
-import static android.view.WindowInsets.Type.navigationBars;
-
import android.app.ActivityManager;
import android.car.Car;
import android.car.drivingstate.CarUxRestrictionsManager;
@@ -25,6 +23,8 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.GestureDetector;
@@ -82,6 +82,7 @@
private final StatusBarStateController mStatusBarStateController;
private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
private final NotificationVisibilityLogger mNotificationVisibilityLogger;
+ private final int mNavBarHeight;
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -138,7 +139,10 @@
mStatusBarStateController = statusBarStateController;
mNotificationVisibilityLogger = notificationVisibilityLogger;
+ mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.car_bottom_navigation_bar_height);
+
mCommandQueue.addCallback(this);
+
// Notification background setup.
mInitialBackgroundAlpha = (float) mResources.getInteger(
R.integer.config_initialNotificationBackgroundAlpha) / 100;
@@ -179,6 +183,27 @@
}
}
+ @Override
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
+ if (mContext.getDisplayId() != displayId) {
+ return;
+ }
+ boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+ int bottomMargin = isKeyboardVisible ? 0 : mNavBarHeight;
+ ViewGroup container = (ViewGroup) getLayout();
+ if (container == null) {
+ // Notification panel hasn't been inflated before. We shouldn't try to update the layout
+ // params.
+ return;
+ }
+
+ ViewGroup.MarginLayoutParams params =
+ (ViewGroup.MarginLayoutParams) container.getLayoutParams();
+ params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
+ container.setLayoutParams(params);
+ }
+
// OverlayViewController
@Override
@@ -204,7 +229,7 @@
@Override
protected int getInsetTypesToFit() {
- return navigationBars();
+ return 0;
}
@Override
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java
index a3a55aa..fe071d5 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/hvac/AdjustableTemperatureViewTest.java
@@ -98,6 +98,48 @@
}
@Test
+ public void setTemp_tempNaN_setsTextToNaNText() {
+ when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+ anyInt())).thenReturn(true);
+ when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+ Float.NaN);
+
+ mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+ TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text);
+ assertEquals(tempText.getText(),
+ getContext().getResources().getString(R.string.hvac_null_temp_text));
+ }
+
+ @Test
+ public void setTemp_tempBelowMin_setsTextToMinTempText() {
+ when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+ anyInt())).thenReturn(true);
+ when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+ getContext().getResources().getFloat(R.dimen.hvac_min_value_celsius));
+
+ mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+ TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text);
+ assertEquals(tempText.getText(),
+ getContext().getResources().getString(R.string.hvac_min_text));
+ }
+
+ @Test
+ public void setTemp_tempAboveMax_setsTextToMaxTempText() {
+ when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
+ anyInt())).thenReturn(true);
+ when(mCarPropertyManager.getFloatProperty(eq(HVAC_TEMPERATURE_SET), anyInt())).thenReturn(
+ getContext().getResources().getFloat(R.dimen.hvac_max_value_celsius));
+
+ mHvacController.addTemperatureViewToController(mAdjustableTemperatureView);
+
+ TextView tempText = mAdjustableTemperatureView.findViewById(R.id.hvac_temperature_text);
+ assertEquals(tempText.getText(),
+ getContext().getResources().getString(R.string.hvac_max_text));
+ }
+
+ @Test
public void setTemperatureToFahrenheit_callsViewSetDisplayInFahrenheit() {
when(mCarPropertyManager.isPropertyAvailable(eq(HVAC_TEMPERATURE_SET),
anyInt())).thenReturn(true);
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 4bc7bf6..462a6a9 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Switserse Duits"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgies"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaars"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaars, foneties"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiaans"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Deens"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noors"</string>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 7a4ffeff..1559fa8 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"የስዊዝ ጀርመን"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ቤልጂየም"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ቡልጋሪያ"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ቡልጋሪያኛ፣ ፎነቲክ"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ጣሊያንኛ"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ዴኒሽ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ኖርዌጂያ"</string>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index a63821e..49fbef9 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ছুইছ জাৰ্মান"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"বেলজিয়ান"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"বুলগেৰিয়ান"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"বুলগেৰিয়ান ফ’নেটিক"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ইটালিয়ান"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ডেনিশ্ব"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ন\'ৰৱেয়ান"</string>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index 16badc4..c5a1e1e 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"İsveçrə Almanı"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belçikalı"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bolqar"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bolqar dili, Fonetika"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"İtalyan"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danimarkalı"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norveçli"</string>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index 95ef459..16f1cb2 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švajcarsko nemačka"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarska"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarska fonetska"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanska"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index 63b899a..6b0523f 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Нямецкая (Швейцарыя)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельгійская"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Балгарская"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Балгарская фанетычная"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Італьянская"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дацкая"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Нарвежская"</string>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index 90b7f6c..a7088c9 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"швейцарски немски"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"белгийски"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"български"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Българска фонетична клавиатура"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"италиански"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"датски"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвежки"</string>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index f6a229c..b92ac8c 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarski njemački"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijski"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarski"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarski, fonetski"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanski"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danski"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveški"</string>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index ec3b247..a5b5e10 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemany suís"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgar"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgar, fonètic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italià"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danès"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruec"</string>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index dbe685c..cf2aecf 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Schweizertysk"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisk"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarsk"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarsk, fonetisk"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiensk"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dansk"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norsk"</string>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index fd7fca0..1e78685 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Schweizerdeutsch"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisch"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarisch"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarisch – phonetisch"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italienisch"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dänisch"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegisch"</string>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 8bdd6f8..eb2cc9b 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Γερμανικά Ελβετίας"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Βελγικά"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Βουλγαρικά"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Βουλγαρικά (Φωνητικό)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Ιταλικά"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Δανικά"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Νορβηγικά"</string>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index 5319f96..e8d6597 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán de Suiza"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danés"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruego"</string>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 95b3b1c..9396e46 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán suizo"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro (fonético)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danés"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruego"</string>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index a5fb8ac..cf28e9f 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Šveitsisaksa"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgia"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaaria"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaaria, foneetiline"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Itaalia"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Taani"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norra"</string>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 1c75a7c..1e080fc 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemana (Suitza)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgikarra"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgariarra"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgariarra, fonetikoa"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiarra"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Daniarra"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegiarra"</string>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index ca7d43a..5cb237e 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"آلمانی سوئیسی"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"بلژیکی"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"بلغاری"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"بلغاری، آوایی"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ایتالیایی"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"دانمارکی"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"نروژی"</string>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index 2878c78..da72106 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"sveitsinsaksa"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgialainen"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulgaria"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bulgaria, foneettinen"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italia"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"tanska"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norja"</string>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index e1ca7cf..40ede04 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemán suízo"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarqués"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noruegués"</string>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index bc2ee83..631fc14 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"સ્વિસ જર્મન"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"બેલ્જિયન"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"બલ્ગેરિયન"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"બલ્ગેરિયન ફોનેટિક"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ઇટાલિયન"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ડેનિશ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"નોર્વેજીયન"</string>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index 0430f86..aff2a37 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarsko-njemačka"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarska"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarska (fonetska)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"talijanska"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index 76d10f5..50d667b4 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"svájci német"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bolgár"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bolgár fonetikus"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"olasz"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dán"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norvég"</string>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index fa4e245..4a6fe2b 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Շվեյցարական գերմաներեն"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Բելգիական"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Բուլղարերեն"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"բուլղարերեն (հնչյունային)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Իտալերեն"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Դանիերեն"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Նորվեգերեն"</string>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index f5d173a..90ba97d 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Jerman Swiss"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgia"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaria"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaria, Fonetik"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italia"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Denmark"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegia"</string>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index 09eedd3..0889b21 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Svissneskt-þýskt"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgískt"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgarskt"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgarskt hljóðritunarlyklaborð"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Ítalskt"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danskt"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norskt"</string>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index e15c01f..77f78c6 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tedesco svizzero"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaro"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgara, fonetica"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danese"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegese"</string>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 4abdf87..52641b2 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"גרמנית שוויצרית"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"בלגית"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"בולגרית"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"בולגרית פונטית"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"איטלקית"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"דנית"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"נורווגית"</string>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index 606ab3c..2961548 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ドイツ語(スイス)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ベルギー語"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ブルガリア語"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ブルガリア語(表音)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"イタリア語"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"デンマーク語"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ノルウェー語"</string>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index b4b1a2d..2ccfeb2 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"შვეიცარიული გერმანული"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ბელგიური"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ბულგარული"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ბულგარული ფონეტიკური"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"იტალიური"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"დანიური"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ნორვეგიული"</string>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index 8f62eb3..1e3c693 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ಸ್ವಿಸ್ ಜರ್ಮನ್"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ಬೆಲ್ಜಿಯನ್"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ಬಲ್ಗೇರಿಯನ್"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ಬಲ್ಗೇರಿಯನ್ ಫೋನೆಟಿಕ್"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ಇಟಾಲಿಯನ್"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ಡ್ಯಾನಿಶ್"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ನಾರ್ವೇಜಿಯನ್"</string>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index b1f6582..1470504 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"독일어(스위스)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"벨기에어"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"불가리아어"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"불가리아어, 표음"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"이탈리아어"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"덴마크어"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"노르웨이어"</string>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index bc521a2..cb9dbb2 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Немис (Швейцария)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Белгия"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгарча, фонетикалык"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Италия"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дания"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвег"</string>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index edb59f3..4ae4b7d 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ສະວິສ ເຢຍລະມັນ"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ເບວຢ້ຽນ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ຮັງກາຣຽນ"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ບັງກາຣຽນ, ການອອກສຽງ"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ອິຕາລຽນ"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ເດັນນິຊ"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ນໍເວກຽນ"</string>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index f33eb42..d2aef7f 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Šveicarijos vokiečių k."</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgų k."</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarų k."</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Fonetinė bulgarų"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italų k."</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danų k."</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegų k."</string>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 4f47a3b..8f3ff0a 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Vācu (Šveice)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Beļģu"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgāru"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgāru, fonētiskā"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Itāļu"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dāņu"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvēģu"</string>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index 65fbf22..9e53443 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"സ്വിസ് ജര്മന്"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ബെൽജിയൻ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ബൾഗേറിയൻ"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ബൾഗേറിയൻ, ഉച്ചാരണശബ്ദം"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ഇറ്റാലിയൻ"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ഡാനിഷ്"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"നോർവീജിയൻ"</string>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index a8fc661..18c2faf 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Швейцарийн Герман"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Бельги"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Болгар"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгар хэл, Авиа зүй"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Итали"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Дани"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Норвеги"</string>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index da6caab..d8788c9 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"स्विस जर्मन"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"बेल्जियन"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"बल्गेरियन"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"बल्गेरियन, फोनेटिक"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"इटालियन"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"डॅनिश"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"नॉर्वेजियन"</string>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 975024b..8bc9b2a 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Jerman Switzerland"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Bahasa Belgium"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bahasa Bulgaria"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bahasa Bulgaria, Fonetik"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Bahasa Itali"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Bahasa Denmark"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Bahasa Norway"</string>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 5484d9d..2672057 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ဆွစ် ဂျာမန်"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ဘယ်လ်ဂျီယန်"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ဘူဂေးရီယန်း"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ဘူလ်ဂေးရီးယား အသံထွက်"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"အီတာလီယန်"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ဒိန်းမတ်"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"နောဝေဂျီယန်"</string>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 5484033..83b87e5 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Sveitsisk standardtysk"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisk"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarsk"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarsk, fonetisk"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiensk"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dansk"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norsk"</string>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index e000a30..6e58490 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Zwitsers Duits"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgisch"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgaars"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgaars, fonetisch"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiaans"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Deens"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Noors"</string>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index 6185ff7..aa16151 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ସୁଇସ୍ ଜର୍ମାନ୍"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ବେଲ୍ଜିଆନ୍"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ବୁଲଗାରିଆନ୍"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ବୁଲଗେରିଆନ୍, ଫୋନେଟିକ୍"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ଇଟାଲିୟାନ୍"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ଡାନିଶ୍"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ନରୱେଜିଆନ୍"</string>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index 97cf28b..7e5a03c 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ਸਵਿਸ ਜਰਮਨ"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"ਬੈਲਜੀਅਨ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"ਬਲਗੇਰੀਅਨ"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ਬਲਗੇਰੀਅਨ, ਧੁਨੀਆਤਮਿਕ"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ਇਤਾਲਵੀ"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ਡੈਨਿਸ਼"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"ਨਾਰਵੇਜੀਅਨ"</string>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index 61819b6..51755e2 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Niemiecki (Szwajcaria)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgijski"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bułgarski"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bułgarski (znaki fonetyczne)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Włoski"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Duński"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norweski"</string>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index 665a1c7..1288688 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão suíço"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index 1ccc644..89bb3e3 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão (Suíça)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 665a1c7..1288688 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Alemão suíço"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belga"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Búlgaro"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Búlgaro, fonético"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Dinamarquês"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norueguês"</string>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index e0b4885..f7ff250 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Germană (Elveția)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiană"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgară"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgară fonetică"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italiană"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Daneză"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegiană"</string>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 41ccf1a..0cb4f34 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"немецкий (Швейцария)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"нидерландский (Бельгия)"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"болгарский"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"болгарский (фонетическая)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"итальянский"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"датский"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвежский"</string>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index 4d355d7..eb3c446 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"ස්විස් ජර්මන්"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"බෙල්ගියන්"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"බල්ගේරියානු"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"බල්ගේරියානු, ශබ්දිම"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ඉතාලි"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ඩෙන්මාර්ක"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"නෝර්වීජියානු"</string>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index c7ff2fd..e7e15b0 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švajčiarske (nemčina)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgické"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bulharské"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulharská fonetická klávesnica"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"talianske"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dánske"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"nórske"</string>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index 68741b4..f4d1e57 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarska nemška"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bolgarska"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bolgarščina (fonetična)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italijanska"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index 2f68903..e3a2043 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"швајцарско немачка"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"белгијска"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"бугарска"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"бугарска фонетска"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"италијанска"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"данска"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвешка"</string>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index b465fa6..097ada4 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tyskt (Schweiz)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiskt"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgariskt"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgariska (fonetiskt)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italienskt"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danskt"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norskt"</string>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 794d907..3257962 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Kijerumani cha Uswisi"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Kibelgiji"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Kibulgaria"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Kibulgaria, Fonetiki"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Kiitaliano"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Kidenmarki"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Kinorwei"</string>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index d40c3e0..c0253e5 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"స్విస్ జర్మన్"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"బెల్జియన్"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"బల్గేరియన్"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"బల్గేరియన్, ఫోనెటిక్"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"ఇటాలియన్"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"డేనిష్"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"నార్వేజియన్"</string>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 5913650..c8e0e62 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"เยอรมันสวิส"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"เบลเยียม"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"บัลแกเรีย"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"ภาษาบัลแกเรีย ตามการออกเสียง"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"อิตาลี"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"เดนมาร์ก"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"นอร์เวย์"</string>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index a89cea5..f093abb 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"İsviçre Almancası"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belçika dili"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarca"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarca, Fonetik"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"İtalyanca"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danca"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norveççe"</string>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index 4b37ca9..d9b58d2 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"німецька (Швейцарія)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"бельгійська"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"болгарська"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Болгарська (фонетична)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"італійська"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"данська"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"норвезька"</string>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index ca42086..2bff7ed 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"سوئس جرمن"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"بیلجیئن"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"بلغاریائی"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"بلغاریائی، فونیٹک"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"اطالوی"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"ڈینش"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"نارویجین"</string>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 77a06b5..9245aeb 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Nemis (Shveytsariya)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgiyancha"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bolgarcha"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bolgar, fonetik"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italyancha"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Datcha"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvegcha"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index fd570ef..1b42ece 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Tiếng Đức Thụy Sĩ"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Tiếng Bỉ"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Tiếng Bungary"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Ngữ âm"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Tiếng Ý"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Tiếng Đan Mạch"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Tiếng Na Uy"</string>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index afc373a..aa75605 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"瑞士德语"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利时语"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亚语"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亚语,注音"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"意大利语"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麦语"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威语"</string>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index 775fa2a..dc824b9 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"德文(瑞士)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利時文"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亞文"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亞文 (拼音)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"意大利文"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麥文"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威文"</string>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index b4a059c..c2714da 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"德文 (瑞士)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"比利時式"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"保加利亞文"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"保加利亞文 (拼音)"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"義大利文"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"丹麥文"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"挪威文"</string>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 0a2499a..3af1da1 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Isi-Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Isi-Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Isi-Bulgarian"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Isi-Bulgarian, Ifonetiki"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Isi-Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Isi-Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Isi-Norwegian"</string>
diff --git a/packages/SettingsLib/res/values-hy/arrays.xml b/packages/SettingsLib/res/values-hy/arrays.xml
index 2696f5a..a279872 100644
--- a/packages/SettingsLib/res/values-hy/arrays.xml
+++ b/packages/SettingsLib/res/values-hy/arrays.xml
@@ -55,7 +55,7 @@
</string-array>
<string-array name="hdcp_checking_summaries">
<item msgid="4045840870658484038">"Երբեք չօգտագործել HDCP ստուգումը"</item>
- <item msgid="8254225038262324761">"Օգտագործել HDCP-ը` միայն DRM-ի բովանդակությունը ստուգելու համար"</item>
+ <item msgid="8254225038262324761">"Օգտագործել HDCP-ը՝ միայն DRM-ի բովանդակությունը ստուգելու համար"</item>
<item msgid="6421717003037072581">"Միշտ օգտագործել HDCP ստուգումը"</item>
</string-array>
<string-array name="bt_hci_snoop_log_entries">
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index ffd95a4..e434cac 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -343,7 +343,7 @@
<string name="show_hw_layers_updates_summary" msgid="5850955890493054618">"Թարմացվելիս ընդգծել սարքաշարի ծածկույթները կանաչ գույնով"</string>
<string name="debug_hw_overdraw" msgid="8944851091008756796">"Վրիպազերծել GPU գերազանցումները"</string>
<string name="disable_overlays" msgid="4206590799671557143">"Կասեցնել HW վրադրումները"</string>
- <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն` էկրանի կազմման համար"</string>
+ <string name="disable_overlays_summary" msgid="1954852414363338166">"Միշտ օգտագործել GPU-ն՝ էկրանի կազմման համար"</string>
<string name="simulate_color_space" msgid="1206503300335835151">"Նմանակել գունատարածքը"</string>
<string name="enable_opengl_traces_title" msgid="4638773318659125196">"Ակտիվացնել OpenGL հետքերը"</string>
<string name="usb_audio_disable_routing" msgid="3367656923544254975">"Անջատել USB աուդիո երթուղումը"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
index f506b7c..0bde5c0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/OWNERS
@@ -1,7 +1,9 @@
# Default reviewers for this and subdirectories.
-qal@google.com
+andychou@google.com
arcwang@google.com
-govenliu@google.com
asapperstein@google.com
+goldmanj@google.com
+qal@google.com
+wengsu@google.com
-# Emergency approvers in case the above are not available
\ No newline at end of file
+# Emergency approvers in case the above are not available
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index bcd2ff7..4db61b0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -175,6 +175,8 @@
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
Settings.Secure.PANIC_GESTURE_ENABLED,
Settings.Secure.PANIC_SOUND_ENABLED,
- Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED
+ Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 3630f25..1fde40c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -264,5 +264,8 @@
VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.PANIC_SOUND_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(
+ Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS, NONE_NEGATIVE_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index e7ec8b4..bc66601 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1875,6 +1875,15 @@
SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
p.end(assistToken);
+ final long assistHandlesToken = p.start(SecureSettingsProto.ASSIST_HANDLES);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ SecureSettingsProto.AssistHandles.LEARNING_TIME_ELAPSED_MILLIS);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ SecureSettingsProto.AssistHandles.LEARNING_EVENT_COUNT);
+ p.end(assistHandlesToken);
+
final long autofillToken = p.start(SecureSettingsProto.AUTOFILL);
dumpSetting(s, p,
Settings.Secure.AUTOFILL_SERVICE,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 91510f6..9788b30 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -228,6 +228,7 @@
Settings.Global.DEVELOPMENT_FORCE_RTL,
Settings.Global.DEVELOPMENT_ENABLE_SIZECOMPAT_FREEFORM,
Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR,
+ Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV,
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.BATTERY_SAVER_ADAPTIVE_CONSTANTS,
Settings.Global.BATTERY_SAVER_CONSTANTS,
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index d69f3d6..504e18a 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -587,15 +587,19 @@
}
private Uri getCurrentlySelectedRingtoneUri() {
- if (getCheckedItem() == mDefaultRingtonePos) {
- // Use the default Uri that they originally gave us.
- return mUriForDefaultItem;
- } else if (getCheckedItem() == mSilentPos) {
- // Use a null Uri for the 'Silent' item.
- return null;
- } else {
- return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
- }
+ if (getCheckedItem() == POS_UNKNOWN) {
+ // When the getCheckItem is POS_UNKNOWN, it is not the case we expected.
+ // We return null for this case.
+ return null;
+ } else if (getCheckedItem() == mDefaultRingtonePos) {
+ // Use the default Uri that they originally gave us.
+ return mUriForDefaultItem;
+ } else if (getCheckedItem() == mSilentPos) {
+ // Use a null Uri for the 'Silent' item.
+ return null;
+ } else {
+ return mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(getCheckedItem()));
+ }
}
private void saveAnyPlayingRingtone() {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a1b9dcd..cf78a13 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -776,6 +776,7 @@
android:exported="true">
<intent-filter>
<action android:name="com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG" />
+ <action android:name="com.android.systemui.action.DISMISS_MEDIA_OUTPUT_DIALOG" />
</intent-filter>
</receiver>
diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml
index 24b5c23..52b7fab 100644
--- a/packages/SystemUI/res-keyguard/values-km/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-km/strings.xml
@@ -100,7 +100,7 @@
<string name="kg_pin_accepted" msgid="1625501841604389716">"កូដត្រូវបានទទួលយក!"</string>
<string name="keyguard_carrier_default" msgid="6359808469637388586">"គ្មានសេវាទេ។"</string>
<string name="accessibility_ime_switch_button" msgid="9082358310194861329">"ប្ដូរវិធីបញ្ចូល"</string>
- <string name="airplane_mode" msgid="2528005343938497866">"មុខងារពេលជិះយន្តហោះ"</string>
+ <string name="airplane_mode" msgid="2528005343938497866">"ពេលជិះយន្តហោះ"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"តម្រូវឲ្យប្រើលំនាំ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"តម្រូវឲ្យបញ្ចូលកូដ PIN បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
<string name="kg_prompt_reason_restart_password" msgid="8061279087240952002">"តម្រូវឲ្យបញ្ចូលពាក្យសម្ងាត់ បន្ទាប់ពីឧបករណ៍ចាប់ផ្តើមឡើងវិញ"</string>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media.xml b/packages/SystemUI/res/drawable-television/ic_volume_media.xml
index e43c4b4..6a368d5 100644
--- a/packages/SystemUI/res/drawable-television/ic_volume_media.xml
+++ b/packages/SystemUI/res/drawable-television/ic_volume_media.xml
@@ -16,11 +16,11 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/tv_volume_icons_size"
+ android:height="@dimen/tv_volume_icons_size"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@color/tv_volume_dialog_accent"
- android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
+ android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM10,8.83v6.34L7.83,13L5,13v-2h2.83L10,8.83zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77 0,-4.28 -2.99,-7.86 -7,-8.77z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml
index 0f6dc95..6eb944f 100644
--- a/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml
+++ b/packages/SystemUI/res/drawable-television/ic_volume_media_low.xml
@@ -16,11 +16,13 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/tv_volume_icons_size"
+ android:height="@dimen/tv_volume_icons_size"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
- <path
- android:fillColor="@color/tv_volume_dialog_accent"
- android:pathData="M3,15V9H7L12,4V20L7,15H3ZM14,7.97C15.48,8.71 16.5,10.23 16.5,12C16.5,13.77 15.48,15.29 14,16.02V7.97Z"/>
+ <group android:translateX="-2">
+ <path
+ android:fillColor="@color/tv_volume_dialog_accent"
+ android:pathData="M16,7.97v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02 0,-1.77 -1.02,-3.29 -2.5,-4.03zM5,9v6h4l5,5L14,4L9,9L5,9zM12,8.83v6.34L9.83,13L7,13v-2h2.83L12,8.83z"/>
+ </group>
</vector>
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml
index 4b59e13..b683089 100644
--- a/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml
+++ b/packages/SystemUI/res/drawable-television/ic_volume_media_mute.xml
@@ -16,12 +16,13 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="@dimen/tv_volume_icons_size"
+ android:height="@dimen/tv_volume_icons_size"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
- <path
- android:fillColor="@color/tv_volume_dialog_accent"
- android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z"/>
+ <group android:translateX="-4">
+ <path
+ android:fillColor="@color/tv_volume_dialog_accent"
+ android:pathData="M14,8.83v6.34L11.83,13H9v-2h2.83L14,8.83M16,4l-5,5H7v6h4l5,5V4z"/>
+ </group>
</vector>
-
diff --git a/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml b/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml
new file mode 100644
index 0000000..7a44aa6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-television/ic_volume_media_off.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/tv_volume_icons_size"
+ android:height="@dimen/tv_volume_icons_size"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="@color/tv_volume_dialog_accent"
+ android:pathData="M4.34,2.93L2.93,4.34 7.29,8.7 7,9L3,9v6h4l5,5v-6.59l4.18,4.18c-0.65,0.49 -1.38,0.88 -2.18,1.11v2.06c1.34,-0.3 2.57,-0.92 3.61,-1.75l2.05,2.05 1.41,-1.41L4.34,2.93zM10,15.17L7.83,13L5,13v-2h2.83l0.88,-0.88L10,11.41v3.76zM19,12c0,0.82 -0.15,1.61 -0.41,2.34l1.53,1.53c0.56,-1.17 0.88,-2.48 0.88,-3.87 0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM12,4l-1.88,1.88L12,7.76zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v1.79l2.48,2.48c0.01,-0.08 0.02,-0.16 0.02,-0.24z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_off.xml b/packages/SystemUI/res/drawable/ic_volume_media_off.xml
new file mode 100644
index 0000000..875b7b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_media_off.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@drawable/ic_volume_media_mute" />
+</selector>
diff --git a/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml b/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml
deleted file mode 100644
index 0334875..0000000
--- a/packages/SystemUI/res/drawable/tv_rect_dark_right_rounded.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <corners
- android:bottomRightRadius="8dp"
- android:topRightRadius="8dp" />
- <solid android:color="@color/tv_audio_recording_indicator_background" />
-
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/tv_volume_row_seek_bar.xml b/packages/SystemUI/res/drawable/tv_volume_row_seek_bar.xml
new file mode 100644
index 0000000..fe76b63
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_volume_row_seek_bar.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/tv_volume_dialog_seek_bar_background" />
+ <corners android:radius="@dimen/tv_volume_seek_bar_width" />
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/tv_volume_dialog_seek_bar_fill" />
+ <corners android:radius="@dimen/tv_volume_seek_bar_width" />
+ </shape>
+ </clip>
+ </item>
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml b/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml
new file mode 100644
index 0000000..588782d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+ <solid android:color="@color/tv_volume_dialog_accent" />
+ <size android:width="@dimen/tv_volume_seek_bar_thumb_diameter"
+ android:height="@dimen/tv_volume_seek_bar_thumb_diameter" />
+ <stroke android:width="@dimen/tv_volume_seek_bar_thumb_focus_ring_width"
+ android:color="@color/tv_volume_dialog_seek_thumb_focus_ring"/>
+ <item name="android:shadowColor">@color/tv_volume_dialog_seek_thumb_shadow</item>
+ <item name="android:shadowRadius">@dimen/tv_volume_seek_bar_thumb_shadow_radius</item>
+ <item name="android:shadowDy">@dimen/tv_volume_seek_bar_thumb_shadow_dy</item>
+</shape>
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index d28d566..4f6cb01 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -40,7 +40,7 @@
android:fontFeatureSettings="tnum"
android:background="@drawable/tv_volume_dialog_circle"
android:textSize="@dimen/tv_volume_number_text_size"
- android:textColor="@color/accent_tint_color_selector"/>
+ android:textColor="@color/tv_volume_dialog_accent"/>
<TextView
android:id="@+id/volume_row_header"
android:layout_width="wrap_content"
@@ -61,6 +61,12 @@
android:layout_width="@dimen/volume_dialog_row_height"
android:layout_height="match_parent"
android:layout_gravity="center"
+ android:layoutDirection="ltr"
+ android:maxHeight="@dimen/tv_volume_seek_bar_width"
+ android:minHeight="@dimen/tv_volume_seek_bar_width"
+ android:thumb="@drawable/tv_volume_row_seek_thumb"
+ android:progressDrawable="@drawable/tv_volume_row_seek_bar"
+ android:splitTrack="false"
android:rotation="270" />
</FrameLayout>
<com.android.keyguard.AlphaOptimizedImageButton
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 0229e6e..73beefc 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -32,7 +32,7 @@
android:id="@+id/header_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingEnd="16dp"/>
+ android:paddingEnd="@dimen/media_output_dialog_header_icon_padding"/>
<LinearLayout
android:layout_width="match_parent"
@@ -70,36 +70,14 @@
android:id="@+id/device_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
android:orientation="vertical">
- <View
- android:layout_width="match_parent"
- android:layout_height="12dp"/>
-
- <include
- layout="@layout/media_output_list_item"
- android:id="@+id/group_item_controller"
- android:visibility="gone"/>
-
- <View
- android:id="@+id/group_item_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?android:attr/listDivider"
- android:visibility="gone"/>
-
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_result"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"/>
-
- <View
- android:id="@+id/list_bottom_padding"
- android:layout_width="match_parent"
- android:layout_height="12dp"/>
</LinearLayout>
<View
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index fd89c0b..10ad829 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -34,7 +34,7 @@
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:clipChildren="false"
- android:paddingTop="11dp"
+ android:paddingTop="@dimen/notification_guts_header_top_padding"
android:clipToPadding="true">
<ImageView
android:id="@+id/conversation_icon"
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index 92b3ff3..0a33d5e 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -30,10 +30,11 @@
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_guts_conversation_header_height"
+ android:layout_height="wrap_content"
android:gravity="center_vertical"
android:clipChildren="false"
- android:clipToPadding="false">
+ android:paddingTop="@dimen/notification_guts_header_top_padding"
+ android:clipToPadding="true">
<ImageView
android:id="@+id/pkg_icon"
android:layout_width="@dimen/notification_guts_conversation_icon_size"
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index c353d08..af66f8b 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -30,10 +30,11 @@
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
- android:layout_height="@dimen/notification_guts_conversation_header_height"
+ android:layout_height="wrap_content"
android:gravity="center_vertical"
android:clipChildren="false"
- android:clipToPadding="false">
+ android:paddingTop="@dimen/notification_guts_header_top_padding"
+ android:clipToPadding="true">
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/notification_guts_conversation_icon_size"
diff --git a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
index f9336a54..b62018d 100644
--- a/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
+++ b/packages/SystemUI/res/layout/tv_audio_recording_indicator.xml
@@ -22,80 +22,17 @@
android:padding="12dp">
<FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_width="34dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center"
+ android:background="@drawable/tv_rect_shadow_rounded">
- <LinearLayout
- android:id="@+id/icon_texts_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent">
-
- <View
- android:id="@+id/icon_container_bg"
- android:layout_width="50dp"
- android:layout_height="match_parent"
- android:background="@drawable/tv_rect_dark_left_rounded"/>
-
- <FrameLayout
- android:id="@+id/icon_mic"
- android:layout_width="34dp"
- android:layout_height="24dp"
- android:layout_gravity="center"
- android:background="@drawable/tv_rect_shadow_rounded">
-
- <ImageView
- android:layout_width="13dp"
- android:layout_height="13dp"
- android:layout_gravity="center"
- android:background="@drawable/tv_ic_mic_white"/>
- </FrameLayout>
-
- </FrameLayout>
-
- <LinearLayout
- android:id="@+id/texts_container"
- android:layout_width="wrap_content"
- android:layout_height="47dp"
- android:background="@color/tv_audio_recording_indicator_background"
- android:orientation="vertical"
- android:visibility="visible">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="14dp"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="1dp"
- android:text="@string/mic_active"
- android:textColor="@android:color/white"
- android:fontFamily="sans-serif"
- android:textSize="10dp"/>
-
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="14dp"
- android:singleLine="true"
- android:text="SomeApplication accessed your microphone"
- android:textColor="@android:color/white"
- android:fontFamily="sans-serif"
- android:textSize="8dp"/>
-
- </LinearLayout>
-
- </LinearLayout>
+ <ImageView
+ android:layout_width="13dp"
+ android:layout_height="13dp"
+ android:layout_gravity="center"
+ android:src="@drawable/tv_ic_mic_white"/>
</FrameLayout>
- <View
- android:id="@+id/bg_end"
- android:layout_width="12dp"
- android:layout_height="47dp"
- android:background="@drawable/tv_rect_dark_right_rounded"
- android:visibility="visible"/>
-
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/window_magnifier_view.xml b/packages/SystemUI/res/layout/window_magnifier_view.xml
index 6ced978..efd24c7 100644
--- a/packages/SystemUI/res/layout/window_magnifier_view.xml
+++ b/packages/SystemUI/res/layout/window_magnifier_view.xml
@@ -17,24 +17,26 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
+ android:layout_height="wrap_content"
+ android:screenReaderFocusable="true">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/magnification_outer_border_margin"
+ android:importantForAccessibility="no"
android:background="@android:color/black"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/magnification_inner_border_margin"
+ android:importantForAccessibility="no"
android:background="@color/magnification_border_color"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:importantForAccessibility="noHideDescendants">
<View
android:id="@+id/left_handle"
@@ -76,6 +78,7 @@
android:layout_margin="@dimen/magnification_outer_border_margin"
android:layout_gravity="right|bottom"
android:scaleType="center"
+ android:importantForAccessibility="no"
android:src="@drawable/ic_move_magnification"/>
</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 556c390..72dfe74 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Wys laeprioriteit-kennisgewingikone"</string>
<string name="other" msgid="429768510980739978">"Ander"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"verwyder teël"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"voeg teël aan einde by"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Skuif teël"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Voeg teël by"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Skuif na <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Voeg by posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kitsinstellingswysiger."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-kennisgewing: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Maak instellings oop."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c846297..0ec236d 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"አነስተኛ ቅድሚያ ያላቸው የማሳወቂያ አዶዎችን አሳይ"</string>
<string name="other" msgid="429768510980739978">"ሌላ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ሰቅ አስወግድ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ሰቅ መጨረሻው ላይ አክል"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ሰቁን ውሰድ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ሰቅ ያክሉ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ውሰድ"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ቦታ አክል"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"የ<xliff:g id="POSITION">%1$d</xliff:g> አቀማመጥ"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"የፈጣን ቅንብሮች አርታዒ።"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"የ<xliff:g id="ID_1">%1$s</xliff:g> ማሳወቂያ፦ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ቅንብሮችን ክፈት።"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 785f8fe..e1d1950 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -899,20 +899,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string>
<string name="other" msgid="429768510980739978">"غير ذلك"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة البطاقة"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"إضافة بطاقة إلى النهاية"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل البطاقة"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"إضافة بطاقة"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"الموضع: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"برنامج تعديل الإعدادات السريعة."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"إشعار <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"فتح الإعدادات."</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 059c4bc..61ba6dc 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"কম গুৰুত্বপূৰ্ণ জাননীৰ আইকনসমূহ দেখুৱাওক"</string>
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল আঁতৰাবলৈ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"টাইল শেষত যোগ দিবলৈ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল স্থানান্তৰ কৰক"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"টাইল যোগ দিয়ক"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰলৈ স্থানান্তৰ কৰক"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থানত যোগ দিয়ক"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থান"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ক্ষিপ্ৰ ছেটিংসমূহৰ সম্পাদক।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> জাননী: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ছেটিংসমূহ খোলক।"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 2381d33..bee0e5f 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Aşağı prioritet bildiriş işarələrini göstərin"</string>
<string name="other" msgid="429768510980739978">"Digər"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"lövhəni silin"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"sona lövhə əlavə edin"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Lövhəni köçürün"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lövhə əlavə edin"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə köçürün"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə əlavə edin"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyi"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sürətli ayarlar redaktoru."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildiriş: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları açın."</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 55abfda..5966356 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -884,20 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obaveštenja niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Drugo"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklonili pločicu"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodali pločicu na kraj"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premestite pločicu"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodajte pločicu"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premestite na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajte na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozicija"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač za Brza podešavanja."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obaveštenja za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori Podešavanja."</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 47f9861..cedc375 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Паказваць значкі апавяшчэнняў з нізкім прыярытэтам"</string>
<string name="other" msgid="429768510980739978">"Іншае"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"выдаліць плітку"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"дадаць плітку ў канец"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перамясціць плітку"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Дадаць плітку"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Пазіцыя <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Рэдактар хуткіх налад."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Апавяшчэнне <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Адкрыць налады."</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index efe1267..1408840 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показване на иконите за известията с нисък приоритет"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"премахване на панел"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавяне на панел в края"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместване на панел"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавяне на панел"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместване към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавяне към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор за бързи настройки."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известие от <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отваряне на настройките."</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 811a971..41a2671 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -884,20 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavještenja niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Ostalo"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodavanje kartice na kraj"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pomjeranje kartice"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodavanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pomjeranje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivanje brzih postavki"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavještenje: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvori postavke."</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 793c44b..783f7877 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -92,10 +92,10 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processant gravació de pantalla"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Notificació en curs d\'una sessió de gravació de la pantalla"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Vols iniciar la gravació?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"Quan graves contingut, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou les contrasenyes, la informació de pagament, les fotos, els missatges i l\'àudio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"Durant la gravació, el sistema Android pot capturar qualsevol informació sensible que es mostri a la pantalla o que es reprodueixi al dispositiu. Això inclou contrasenyes, informació de pagament, fotos, missatges i àudio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Grava l\'àudio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Àudio del dispositiu"</string>
- <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sons del dispositiu, com ara la música, les trucades i els sons de trucada"</string>
+ <string name="screenrecord_device_audio_description" msgid="4922694220572186193">"So del dispositiu, com ara música, trucades i sons de trucada"</string>
<string name="screenrecord_mic_label" msgid="2111264835791332350">"Micròfon"</string>
<string name="screenrecord_device_audio_and_mic_label" msgid="1831323771978646841">"Àudio del dispositiu i micròfon"</string>
<string name="screenrecord_start" msgid="330991441575775004">"Inicia"</string>
@@ -387,7 +387,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"La Wi‑Fi no està connectada"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"Brillantor"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"AUTOMÀTICA"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix els colors"</string>
+ <string name="quick_settings_inversion_label" msgid="5078769633069667698">"Inverteix colors"</string>
<string name="quick_settings_color_space_label" msgid="537528291083575559">"Mode de correcció de color"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"Més opcions"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Fet"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string>
<string name="other" msgid="429768510980739978">"Altres"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"afegir una targeta al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou la targeta"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix una targeta"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuració ràpida."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Obre la configuració."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1d92389..f2e8a3f 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for notifikationer med lav prioritet"</string>
<string name="other" msgid="429768510980739978">"Andet"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjern kortet"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"føj kortet til slutningen"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flyt kortet"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tilføj et kort"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til placering <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Placering <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsværktøj til Kvikmenu."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-notifikation: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åbn Indstillinger."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 5976a1c..887aad5 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Symbole für Benachrichtigungen mit einer niedrigen Priorität anzeigen"</string>
<string name="other" msgid="429768510980739978">"Sonstiges"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"die Kachel zu entfernen"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"die Kachel am Ende hinzuzufügen"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kachel verschieben"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Kachel hinzufügen"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Auf Position <xliff:g id="POSITION">%1$d</xliff:g> verschieben"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Zur Position <xliff:g id="POSITION">%1$d</xliff:g> hinzufügen"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor für Schnelleinstellungen."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Benachrichtigung von <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Einstellungen öffnen."</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 1eae5d4..1b912c7 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Εμφάνιση εικονιδίων ειδοποιήσεων χαμηλής προτεραιότητας"</string>
<string name="other" msgid="429768510980739978">"Άλλο"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"κατάργηση πλακιδίου"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"προσθήκη πλακιδίου στο τέλος"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Μετακίνηση πλακιδίου"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Προσθήκη πλακιδίου"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Μετακίνηση στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Προσθήκη στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Επεξεργασία γρήγορων ρυθμίσεων."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ειδοποίηση <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Άνοιγμα ρυθμίσεων."</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 4ea0341..065c17b 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 3916e58..1cc6625 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 4ea0341..065c17b 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 4ea0341..065c17b 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -92,7 +92,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"Processing screen recording"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"Start recording?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"While recording, Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"While recording, the Android System can capture any sensitive information that’s visible on your screen or played on your device. This includes passwords, payment info, photos, messages and audio."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"Record audio"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"Device audio"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"Sound from your device, like music, calls and ringtones"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index b1ee41f..dfd42c3 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Quitar tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Agregar tarjeta al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Agregar tarjeta"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Agregar a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de Configuración rápida"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir Configuración"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 7e5361b..20b50ab 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar icono"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"añadir icono al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover icono"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Añadir icono"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de ajustes rápidos."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir ajustes."</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 78de793..ced0604 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Kuva madala prioriteediga märguande ikoonid"</string>
<string name="other" msgid="429768510980739978">"Muu"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"paani eemaldamiseks"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"paani lõppu lisamiseks"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Teisalda paan"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lisa paan"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Teisaldamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Asend <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kiirseadete redigeerija."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Teenuse <xliff:g id="ID_1">%1$s</xliff:g> märguanne: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ava seaded."</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b5d8444..c1ab20c 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Erakutsi lehentasun txikiko jakinarazpenen ikonoak"</string>
<string name="other" msgid="429768510980739978">"Beste bat"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"gehitu lauza amaieran"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Aldatu tokiz lauza"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Gehitu lauza"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenera"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren kokapenean"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kokapena: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ezarpen bizkorren editorea."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> zerbitzuaren jakinarazpena: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ireki ezarpenak."</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index d453f91..f6cf179 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"نمایش نمادهای اعلان کماهمیت"</string>
<string name="other" msgid="429768510980739978">"موارد دیگر"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"برداشتن کاشی"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"افزودن کاشی به انتها"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"انتقال کاشی"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"افزودن کاشی"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"انتقال به <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"افزودن به موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ویرایشگر تنظیمات سریع."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"اعلان <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"باز کردن تنظیمات."</string>
@@ -967,7 +960,7 @@
<string name="slice_permission_text_2" msgid="6758906940360746983">"- میتواند در <xliff:g id="APP">%1$s</xliff:g> اقدام انجام دهد"</string>
<string name="slice_permission_checkbox" msgid="4242888137592298523">"به <xliff:g id="APP">%1$s</xliff:g> اجازه داده شود تکههایی از برنامهها نشان دهد"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"مجاز"</string>
- <string name="slice_permission_deny" msgid="6870256451658176895">"رد کردن"</string>
+ <string name="slice_permission_deny" msgid="6870256451658176895">"مجاز نبودن"</string>
<string name="auto_saver_title" msgid="6873691178754086596">"برای زمانبندی «بهینهسازی باتری» ضربه بزنید"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"وقتی باتری روبهاتمام است، بهینهسازی باتری را روشن کنید"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"نه متشکرم"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index dde2449..3d9a6db 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -379,7 +379,7 @@
<string name="quick_settings_wifi_on_label" msgid="2489928193654318511">"Wi-Fi on käytössä"</string>
<string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Ei Wi-Fi-verkkoja käytettävissä"</string>
<string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Otetaan käyttöön…"</string>
- <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön suoratoisto"</string>
+ <string name="quick_settings_cast_title" msgid="2279220930629235211">"Näytön striimaus"</string>
<string name="quick_settings_casting" msgid="1435880708719268055">"Lähetetään"</string>
<string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"Nimetön laite"</string>
<string name="quick_settings_cast_device_default_description" msgid="2580520859212250265">"Valmis lähetystä varten"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Näytä vähemmän tärkeät ilmoituskuvakkeet"</string>
<string name="other" msgid="429768510980739978">"Muu"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"poista kiekko"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"lisää kiekko loppuun"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Siirrä kiekkoa"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lisää kiekko"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Siirrä paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisää paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Paikka <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Pika-asetusten muokkausnäkymä"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Ilmoitus kohteesta <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Avaa asetukset."</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index aba2d8f..9075a69 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconas das notificacións que teñan baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarxeta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"engadir tarxeta ao final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover tarxeta"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Engadir tarxeta"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engadir á posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configuración rápida."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificación de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configuración."</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 1acef35..e8b4460 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"ઓછી પ્રાધાન્યતાનું નોટિફિકેશન આઇકન બતાવો"</string>
<string name="other" msgid="429768510980739978">"અન્ય"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ટાઇલ કાઢી નાખો"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ટાઇલને અંતે ઉમેરો"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ટાઇલ ખસેડો"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ટાઇલ ઉમેરો"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> પર ખસેડો"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"જગ્યા પર <xliff:g id="POSITION">%1$d</xliff:g> ઉમેરો"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"જગ્યા <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ઝડપી સેટિંગ્સ સંપાદક."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> નોટિફિકેશન: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"સેટિંગ્સ ખોલો."</string>
diff --git a/packages/SystemUI/res/values-h740dp-port/dimens.xml b/packages/SystemUI/res/values-h740dp-port/dimens.xml
new file mode 100644
index 0000000..966066f
--- /dev/null
+++ b/packages/SystemUI/res/values-h740dp-port/dimens.xml
@@ -0,0 +1,27 @@
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <dimen name="qs_tile_height">106dp</dimen>
+ <dimen name="qs_tile_margin_vertical">24dp</dimen>
+
+ <!-- The height of the qs customize header. Should be
+ (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) -
+ (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
+ -->
+ <dimen name="qs_customize_header_min_height">46dp</dimen>
+ <dimen name="qs_tile_margin_top">18dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9b3f997..649299b 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -884,20 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obavijesti niskog prioriteta"</string>
<string name="other" msgid="429768510980739978">"Ostalo"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodavanje kartice na kraj"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premještanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodavanje kartice"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premještanje u prostoriju <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Uređivač brzih postavki."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> obavijest: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvaranje postavki."</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 7f4fa9d..f658bb7 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Alacsony prioritású értesítési ikonok mutatása"</string>
<string name="other" msgid="429768510980739978">"Egyéb"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"mozaik eltávolításához"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"mozaiknak a végéhez való hozzáadásához"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mozaik áthelyezése"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Mozaik hozzáadása"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Áthelyezés ide: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Hozzáadás a következő pozícióhoz: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. hely"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Gyorsbeállítások szerkesztője"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-értesítések: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Beállítások megnyitása."</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 70cbf49..28c79fb 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string>
<string name="other" msgid="429768510980739978">"Այլ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ավելացնել սալիկ վերջում"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ավելացնել սալիկ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ավելացնել դիրք <xliff:g id="POSITION">%1$d</xliff:g>-ում"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Արագ կարգավորումների խմբագրիչ:"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ծանուցում՝ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Բացել կարգավորումները:"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index dc0a512..12f42e8 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -862,7 +862,7 @@
<string name="left_icon" msgid="5036278531966897006">"Ikon kiri"</string>
<string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan tarik untuk menambahkan kartu"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk mengatur ulang kartu"</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk menata ulang kartu"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Tarik ke sini untuk menghapus"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda membutuhkan setidaknya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kartu"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edit"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Tampilkan ikon notifikasi prioritas rendah"</string>
<string name="other" msgid="429768510980739978">"Lainnya"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"menghapus kartu"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"menambahkan kartu ke akhir"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pindahkan kartu"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tambahkan kartu"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pindahkan ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan ke posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor setelan cepat."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifikasi <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka setelan."</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index f79796b..1f8f2aa 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -861,10 +861,10 @@
<string name="right_keycode" msgid="2480715509844798438">"Lykiltákn til hægri"</string>
<string name="left_icon" msgid="5036278531966897006">"Tákn til vinstri"</string>
<string name="right_icon" msgid="1103955040645237425">"Tákn til hægri"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við reitum"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við flísum"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Haltu og dragðu til að endurraða flísum"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dragðu hingað til að fjarlægja"</string>
- <string name="drag_to_remove_disabled" msgid="933046987838658850">"Reitirnir mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
+ <string name="drag_to_remove_disabled" msgid="933046987838658850">"Flísarnar mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Breyta"</string>
<string name="tuner_time" msgid="2450785840990529997">"Tími"</string>
<string-array name="clock_options">
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string>
<string name="other" msgid="429768510980739978">"Annað"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja reit"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta reit við aftast"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa reit"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta reit við"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Staða <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Flýtistillingaritill."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> tilkynning: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Opna stillingar."</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 3b99dc2..eb76a4b 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra icone di notifiche con priorità bassa"</string>
<string name="other" msgid="429768510980739978">"Altro"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"rimuovere il riquadro"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"aggiungere il riquadro alla fine"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Sposta riquadro"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Aggiungi riquadro"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Sposta nella posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Aggiungi alla posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor di impostazioni rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notifica di <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Apri le impostazioni."</string>
@@ -975,7 +968,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"Il Risparmio energetico verrà attivato automaticamente quando la carica della batteria sarà inferiore a <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Impostazioni"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Esegui dump heap SysUI"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump heap SysUI"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"L\'app <xliff:g id="APP">%1$s</xliff:g> sta usando <xliff:g id="TYPES_LIST">%2$s</xliff:g>."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Le app stanno usando <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 3bb8ea5..31e6bef 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string>
<string name="other" msgid="429768510980739978">"אחר"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת האריח"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"הוספת האריח לקצה"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"הזזת האריח"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"הוספת אריח"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה אל <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"מיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"עורך הגדרות מהירות."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"התראות <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"פתיחת הגדרות."</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 2c55569..b358f3d 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"優先度の低い通知アイコンを表示"</string>
<string name="other" msgid="429768510980739978">"その他"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"タイルを削除"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"タイルを最後に追加"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"タイルを移動"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"タイルを追加"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> に移動"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ポジション <xliff:g id="POSITION">%1$d</xliff:g> に追加"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"クイック設定エディタ"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> の通知: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"設定を開きます。"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index ca5b7da..4e85d60 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"დაბალი პრიორიტეტის მქონე შეტყობინებების ხატულების ჩვენება"</string>
<string name="other" msgid="429768510980739978">"სხვა"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"მოზაიკის ფილის წაშლა"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ფილის ბოლოში დამატება"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"მოზაიკის გადატანა"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"მოზაიკის დამატება"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"გადატანა <xliff:g id="POSITION">%1$d</xliff:g>-ზე"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"დამატება პოზიციაზე <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"პოზიცია <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"სწრაფი პარამეტრების რედაქტორი."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> შეტყობინება: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"პარამეტრების გახსნა."</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index a4bf498..f172106 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -865,7 +865,7 @@
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердің ретін өзгерту үшін оларды басып тұрып сүйреңіз"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Керексіздерін осы жерге сүйреңіз"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Кемінде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> бөлшек қажет."</string>
- <string name="qs_edit" msgid="5583565172803472437">"Өңдеу"</string>
+ <string name="qs_edit" msgid="5583565172803472437">"Өзгерту"</string>
<string name="tuner_time" msgid="2450785840990529997">"Уақыт"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Сағаттарды, минуттарды және секундтарды көрсету"</item>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Маңызды емес хабарландыру белгішелерін көрсету"</string>
<string name="other" msgid="429768510980739978">"Басқа"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"бөлшекті өшіру"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"бөлшекті соңына қосу"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Бөлшекті жылжыту"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Бөлшек қосу"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> орнына жылжыту"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> орнына қосу"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> орны"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Жылдам параметрлер өңдегіші."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> хабарландыруы: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Параметрлерді ашу."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 7eaa303..b126a11 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"បង្ហាញរូបការជូនដំណឹងដែលមានអាទិភាពទាប"</string>
<string name="other" msgid="429768510980739978">"ផ្សេងៗ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ដកប្រអប់ចេញ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"បញ្ចូលប្រអប់ទៅខាងចុង"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ផ្លាស់ទីប្រអប់"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"បញ្ចូលប្រអប់"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ផ្លាស់ទីទៅ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"បញ្ចូលទៅទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"កម្មវិធីកែការកំណត់រហ័ស"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ការជូនដំណឹង៖ <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"បើកការកំណត់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 79f8452..475677b 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -387,7 +387,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"ಪ್ರಕಾಶಮಾನ"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"ಸ್ವಯಂ"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಬದಲಾಯಿಸಿ"</string>
+ <string name="quick_settings_inversion_label" msgid="5078769633069667698">"ಬಣ್ಣಗಳನ್ನು ಇನ್ವರ್ಟ್ ಮಾಡಿ"</string>
<string name="quick_settings_color_space_label" msgid="537528291083575559">"ಬಣ್ಣ ತಿದ್ದುಪಡಿ ಮೋಡ್"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"ಹೆಚ್ಚಿನ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"ಮುಗಿದಿದೆ"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"ಕಡಿಮೆ-ಆದ್ಯತೆ ಸೂಚನೆಯ ಐಕಾನ್ಗಳನ್ನು ತೋರಿಸಿ"</string>
<string name="other" msgid="429768510980739978">"ಇತರ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ಟೈಲ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ಕೊನೆಯಲ್ಲಿ ಟೈಲ್ ಸೇರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ಟೈಲ್ ಸರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ಟೈಲ್ ಸೇರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ಇಲ್ಲಿಗೆ ಸರಿಸಿ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸೇರಿಸಿ"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ಸ್ಥಾನ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳ ಎಡಿಟರ್."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ಅಧಿಸೂಚನೆ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 6a755e5..ac7f04e 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -371,7 +371,7 @@
<string name="quick_settings_time_label" msgid="3352680970557509303">"시간"</string>
<string name="quick_settings_user_label" msgid="1253515509432672496">"나"</string>
<string name="quick_settings_user_title" msgid="8673045967216204537">"사용자"</string>
- <string name="quick_settings_user_new_user" msgid="3347905871336069666">"새 사용자"</string>
+ <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string>
<string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
<string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string>
<string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string>
@@ -473,7 +473,7 @@
<string name="accessibility_multi_user_switch_inactive" msgid="383168614528618402">"현재 사용자: <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string>
<string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string>
- <string name="user_new_user_name" msgid="2019166282704195789">"새 사용자"</string>
+ <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string>
<string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
<string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"우선순위가 낮은 알림 아이콘 표시"</string>
<string name="other" msgid="429768510980739978">"기타"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"타일 삭제"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"끝에 타일 추가"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"타일 이동"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"타일 추가"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> 위치로 이동"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> 위치에 추가"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> 위치"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"빠른 설정 편집기"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 알림: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"설정 열기"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index a740d07..d775823 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -862,7 +862,7 @@
<string name="left_icon" msgid="5036278531966897006">"¨Солго¨ сүрөтчөсү"</string>
<string name="right_icon" msgid="1103955040645237425">"¨Оңго¨ сүрөтчөсү"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Керектүү элементтерди сүйрөп келиңиз"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн, кармап туруп, сүйрөңүз"</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердин иретин өзгөртүү үчүн кармап туруп, сүйрөңүз"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Алып салуу үчүн бул жерге сүйрөңүз"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Сизге жок дегенде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> мозаика керек"</string>
<string name="qs_edit" msgid="5583565172803472437">"Түзөтүү"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
<string name="other" msgid="429768510980739978">"Башка"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"плитканы өчүрүү"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"плитканы аягына кошуу"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Плитканы жылдыруу"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Плитка кошуу"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>-позиция"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ыкчам жөндөөлөр түзөткүчү."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> эскертмеси: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Жөндөөлөрдү ачуу."</string>
diff --git a/packages/SystemUI/res/values-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml
index 8237919..95ff59b 100644
--- a/packages/SystemUI/res/values-land-television/dimens.xml
+++ b/packages/SystemUI/res/values-land-television/dimens.xml
@@ -16,10 +16,17 @@
<resources>
<!-- Height of volume bar -->
- <dimen name="volume_dialog_row_height">200dp</dimen>
- <dimen name="volume_dialog_panel_transparent_padding">17dp</dimen>
+ <dimen name="volume_dialog_row_height">190dp</dimen>
+ <dimen name="volume_dialog_row_width">48dp</dimen>
+ <dimen name="volume_dialog_panel_transparent_padding">24dp</dimen>
<dimen name="tv_volume_dialog_bubble_size">36dp</dimen>
- <dimen name="tv_volume_dialog_corner_radius">40dp</dimen>
- <dimen name="tv_volume_dialog_row_padding">5dp</dimen>
- <dimen name="tv_volume_number_text_size">16dp</dimen>
+ <dimen name="tv_volume_dialog_corner_radius">36dp</dimen>
+ <dimen name="tv_volume_dialog_row_padding">6dp</dimen>
+ <dimen name="tv_volume_number_text_size">16sp</dimen>
+ <dimen name="tv_volume_seek_bar_width">4dp</dimen>
+ <dimen name="tv_volume_seek_bar_thumb_diameter">24dp</dimen>
+ <dimen name="tv_volume_seek_bar_thumb_focus_ring_width">8dp</dimen>
+ <dimen name="tv_volume_icons_size">20dp</dimen>
+ <dimen name="tv_volume_seek_bar_thumb_shadow_radius">4.0</dimen>
+ <dimen name="tv_volume_seek_bar_thumb_shadow_dy">4.0</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 1fb2ca0..67642d3 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"ສະແດງໄອຄອນການແຈ້ງເຕືອນຄວາມສຳຄັນຕ່ຳ"</string>
<string name="other" msgid="429768510980739978">"ອື່ນໆ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ລຶບແຜ່ນອອກ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ເພີ່ມແຜ່ນໃສ່ທ້າຍ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ຍ້າຍແຜ່ນ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ເພີ່ມແຜ່ນ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ຍ້າຍໄປ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ຕົວແກ້ໄຂການຕັ້ງຄ່າດ່ວນ"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"ການແຈ້ງເຕືອນ <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ເປີດການຕັ້ງຄ່າ."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index bd24722..e17f6e4 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Rodyti mažo prioriteto pranešimų piktogramas"</string>
<string name="other" msgid="429768510980739978">"Kita"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"pašalintumėte išklotinės elementą"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pridėtumėte išklotinės elementą gale"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Perkelti išklotinės elementą"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pridėti išklotinės elementą"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Perkelkite į <xliff:g id="POSITION">%1$d</xliff:g> poziciją"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridėkite <xliff:g id="POSITION">%1$d</xliff:g> pozicijoje"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> pozicija"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Sparčiųjų nustatymų redagavimo priemonė."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"„<xliff:g id="ID_1">%1$s</xliff:g>“ pranešimas: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atidaryti nustatymus."</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index e8d9a4d..96d563c 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -884,20 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Rādīt zemas prioritātes paziņojumu ikonas"</string>
<string name="other" msgid="429768510980739978">"Citi"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"noņemt elementu"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pievienot elementu beigās"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pārvietot elementu"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pievienot elementu"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pārvietot uz pozīciju numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pievienot elementu pozīcijā numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozīcija numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Ātro iestatījumu redaktors."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> paziņojums: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Atvērt iestatījumus."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 164cb96..f53147e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Прикажувај икони за известувања со низок приоритет"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"отстранување на плочката"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додавање на плочката на крај"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместување на плочката"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додавање плочка"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместување на <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додавање на позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уредник за брзи поставки."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Известување од <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отворете ги поставките."</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index ba379d3..fc68681 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -387,7 +387,7 @@
<string name="quick_settings_cast_no_wifi" msgid="6980194769795014875">"വൈഫൈ കണക്റ്റ് ചെയ്തിട്ടില്ല"</string>
<string name="quick_settings_brightness_dialog_title" msgid="4980669966716685588">"തെളിച്ചം"</string>
<string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="2325362583903258677">"യാന്ത്രികം"</string>
- <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നിറം മാറ്റുക"</string>
+ <string name="quick_settings_inversion_label" msgid="5078769633069667698">"നെഗറ്റീവ് ലുക്ക്"</string>
<string name="quick_settings_color_space_label" msgid="537528291083575559">"വർണ്ണം ശരിയാക്കൽ മോഡ്"</string>
<string name="quick_settings_more_settings" msgid="2878235926753776694">"കൂടുതൽ ക്രമീകരണങ്ങൾ"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"പൂർത്തിയാക്കി"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"പ്രാധാന്യം കുറഞ്ഞ അറിയിപ്പ് ചിഹ്നങ്ങൾ"</string>
<string name="other" msgid="429768510980739978">"മറ്റുള്ളവ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ടൈൽ നീക്കം ചെയ്യുക"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ടൈൽ, അവസാന ഭാഗത്ത് ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ടൈൽ നീക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ടൈൽ ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ലേക്ക് നീക്കുക"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>-ൽ ചേർക്കുക"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"സ്ഥാനം <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ദ്രുത ക്രമീകരണ എഡിറ്റർ."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> അറിയിപ്പ്: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ക്രമീകരണം തുറക്കുക."</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 56c5ef2..1c82e48 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Бага ач холбогдолтой мэдэгдлийн дүрс тэмдгийг харуулах"</string>
<string name="other" msgid="429768510980739978">"Бусад"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"хавтанг хасна уу"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"дуусгахын тулд хавтан нэмэх"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Хавтанг зөөх"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Хавтан нэмэх"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> руу зөөнө үү"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> байрлалд нэмнэ үү"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> байрлал"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Түргэн тохиргоо засварлагч."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> мэдэгдэл: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Тохиргоог нээнэ үү."</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 728065f..b26f89f 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"कमी प्राधान्य सूचना आयकन दर्शवा"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल काढून टाका"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल शेवटच्या स्थानावर जोडा"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल हलवा"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल जोडा"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> यावर हलवा"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> स्थानावर जोडा"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थान <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिंग्ज संपादक."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिंग्ज उघडा."</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 62d845c..92bc807 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Tunjukkan ikon pemberitahuan keutamaan rendah"</string>
<string name="other" msgid="429768510980739978">"Lain-lain"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alih keluar jubin"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"tambahkan jubin pada bahagian hujung"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Alihkan jubin"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tambahkan jubin"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Alih ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan pada kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Kedudukan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor tetapan pantas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Pemberitahuan <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buka tetapan."</string>
@@ -975,7 +968,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"Penjimat Bateri akan dihidupkan secara automatik setelah kuasa bateri kurang daripada <xliff:g id="PERCENTAGE">%d</xliff:g>%%."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Tetapan"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"OK"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Longgok Tmbunn SysUI"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"DumpSys"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"<xliff:g id="APP">%1$s</xliff:g> sedang menggunakan <xliff:g id="TYPES_LIST">%2$s</xliff:g> anda."</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplikasi sedang menggunakan <xliff:g id="TYPES_LIST">%s</xliff:g> anda."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index e72f169..9cc9398 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"အရေးမကြီးသော အကြောင်းကြားချက် သင်္ကေတများ ပြရန်"</string>
<string name="other" msgid="429768510980739978">"အခြား"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"အကွက်ငယ်ကို ဖယ်ရှားရန်"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"အဆုံးတွင် အကွက်ငယ်ထည့်ရန်"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"အကွက်ငယ်ကို ရွှေ့ရန်"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"အကွက်ငယ်ကို ထည့်ရန်"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> သို့ ရွှေ့ရန်"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထားသို့ ပေါင်းထည့်ရန်"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထား"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"မြန်ဆန်သည့် ဆက်တင်တည်းဖြတ်စနစ်"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> အကြောင်းကြားချက် − <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ဆက်တင်များကို ဖွင့်ပါ။"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 41aabcb..92ae689 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for varsler med lav prioritet"</string>
<string name="other" msgid="429768510980739978">"Annet"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjerne infobrikken"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"legge til en infobrikke på slutten"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytt infobrikken"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Legg til en infobrikke"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Legg til posisjonen <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisjon <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigeringsvindu for hurtiginnstillinger."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-varsel: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Åpne innstillingene."</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 0cf09d2..1346874 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकताका सूचना आइकनहरू देखाउनुहोस्"</string>
<string name="other" msgid="429768510980739978">"अन्य"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाउनुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"टाइल अन्त्यमा हाल्नुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल सार्नुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"टाइल हाल्नुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल सारेर <xliff:g id="POSITION">%1$d</xliff:g> मा लैजानुहोस्"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल यो अवस्था <xliff:g id="POSITION">%1$d</xliff:g> मा हाल्नुहोस्"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"स्थिति <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"द्रुत सेटिङ सम्पादक।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> को सूचना: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"सेटिङहरूलाई खोल्नुहोस्।"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 49e097e..139b57b 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Pictogrammen voor meldingen met lage prioriteit weergeven"</string>
<string name="other" msgid="429768510980739978">"Overig"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"tegel verwijderen"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"tegel toevoegen aan einde"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Tegel verplaatsen"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Tegel toevoegen"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Verplaatsen naar <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Toevoegen aan positie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Positie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor voor \'Snelle instellingen\'."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-melding: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Instellingen openen."</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index c3105ff..3973734 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -861,7 +861,7 @@
<string name="right_keycode" msgid="2480715509844798438">"ଡାହାଣ କୀ\'କୋଡ୍"</string>
<string name="left_icon" msgid="5036278531966897006">"ବାମ ଆଇକନ୍"</string>
<string name="right_icon" msgid="1103955040645237425">"ଡାହାଣ ଆଇକନ୍"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଡ଼ିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଗ କରିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ଟାଇଲ୍ ପୁଣି ସଜାଇବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"କମ୍-ଅଗ୍ରାଧିକାର ବିଜ୍ଞପ୍ତି ଆଇକନ୍ ଦେଖାନ୍ତୁ"</string>
<string name="other" msgid="429768510980739978">"ଅନ୍ୟାନ୍ୟ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ଟାଇଲ୍ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ଶେଷରେ ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ଟାଇଲ୍ ମୁଭ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ଟାଇଲ୍ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>କୁ ମୁଭ୍ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ଅବସ୍ଥିତିରେ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ଅବସ୍ଥିତି <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ସେଟିଂସ୍ ଖୋଲନ୍ତୁ।"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index b12dfb3..e727f48a 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੇ ਸੂਚਨਾ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਦਿਖਾਓ"</string>
<string name="other" msgid="429768510980739978">"ਹੋਰ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ਟਾਇਲ ਹਟਾਓ"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ਟਾਇਲ ਨੂੰ ਅੰਤ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ਟਾਇਲ ਨੂੰ ਲਿਜਾਓ"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> \'ਤੇ ਲਿਜਾਓ"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ਸਥਾਨ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਸੰਪਾਦਕ।"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> ਸੂਚਨਾ: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index d195977..f188aaa 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Pokazuj ikony powiadomień o niskim priorytecie"</string>
<string name="other" msgid="429768510980739978">"Inne"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"usunąć kartę"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodać kartę na końcu"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Przenieś kartę"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodaj kartę"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Przenieś do pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodaj w pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozycja <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Edytor szybkich ustawień."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Powiadomienie z aplikacji <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otwórz ustawienia."</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 6f96f37..8c8adae 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o bloco ao final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar bloco"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 93999f6..082e14e 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -861,7 +861,7 @@
<string name="right_keycode" msgid="2480715509844798438">"Código de tecla direito"</string>
<string name="left_icon" msgid="5036278531966897006">"Ícone esquerdo"</string>
<string name="right_icon" msgid="1103955040645237425">"Ícone direito"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Toque sem soltar e arraste para adicionar mosaicos."</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Tocar sem soltar e arrastar para adicionar mosaicos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tocar sem soltar e arrastar para reorganizar os mosaicos"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastar para aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessita de, pelo menos, <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> cartões"</string>
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de prioridade baixa"</string>
<string name="other" msgid="429768510980739978">"Outro"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o cartão"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o cartão ao final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover cartão"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar cartão"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mova para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicione à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de definições rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir as definições."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 6f96f37..8c8adae 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
<string name="other" msgid="429768510980739978">"Outros"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adicionar o bloco ao final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adicionar bloco"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor de configurações rápidas."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificação do <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Abrir configurações."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 9810cc5..4128c21 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -884,20 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afișați pictogramele de notificare cu prioritate redusă"</string>
<string name="other" msgid="429768510980739978">"Altele"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"eliminați cardul"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adăugați cardul la sfârșit"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mutați cardul"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adăugați un card"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mutați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adăugați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificare <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Deschideți setările."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 9f43b5a..66e3ec3 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string>
<string name="other" msgid="429768510980739978">"Другое"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить кнопку быстрого доступа"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"добавить кнопку быстрого доступа в конец"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить кнопку быстрого доступа"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Добавить кнопку быстрого доступа"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор быстрых настроек."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Уведомление <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Открыть настройки."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 700f510..98e8d1f 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"අඩු ප්රමුඛතා දැනුම්දීම් අයිකන පෙන්වන්න"</string>
<string name="other" msgid="429768510980739978">"වෙනත්"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ටයිල් ඉවත් කරන්න"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"අගට ටයිල් එක් කරන්න"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ටයිල් ගෙන යන්න"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ටයිල් එක් කරන්න"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> වෙත ගෙන යන්න"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ස්ථානයට එක් කරන්න"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ස්ථානය <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ඉක්මන් සැකසුම් සංස්කාරකය."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> දැනුම්දීම: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"සැකසීම් විවෘත කරන්න."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 62df35a..d9c6279 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Zobraziť ikony upozornení s nízkou prioritou"</string>
<string name="other" msgid="429768510980739978">"Ďalšie"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstrániť kartu"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"pridať kartu na koniec"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Presunúť kartu"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Pridať kartu"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Presunúť na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridať na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. pozícia"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rýchlych nastavení"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Upozornenie <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Otvoriť nastavenia"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 25bdd65..778938c 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Pokaži ikone obvestil z nizko stopnjo prednosti"</string>
<string name="other" msgid="429768510980739978">"Drugo"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranitev ploščice"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"dodajanje ploščice na konec"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premik ploščice"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Dodajanje ploščice"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premik na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Urejevalnik hitrih nastavitev."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Obvestilo za <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Odpri nastavitve."</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index e117602..90f1c5a 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -884,20 +884,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Прикажи иконе обавештења ниског приоритета"</string>
<string name="other" msgid="429768510980739978">"Друго"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"уклонили плочицу"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додали плочицу на крај"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместите плочицу"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додајте плочицу"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместите на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додајте на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>. позиција"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Уређивач за Брза подешавања."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Обавештења за <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Отвори Подешавања."</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 8559b0b..8918f51 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Visa ikoner för aviseringar med låg prioritet"</string>
<string name="other" msgid="429768510980739978">"Annat"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ta bort ruta"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"lägg till ruta i slutet"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytta ruta"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Lägg till ruta"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytta till <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lägg till på position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redigerare för snabbinställningar."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>-avisering: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Öppna inställningarna."</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 76b86ee..3949f2d 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Onyesha aikoni za arifa zisizo muhimu"</string>
<string name="other" msgid="429768510980739978">"Nyingine"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ondoa kigae"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ongeza kigae mwishoni"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hamisha kigae"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ongeza kigae"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hamishia kwenye <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ongeza kwenye nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Kihariri cha Mipangilio ya haraka."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Arifa kutoka <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Fungua mipangilio."</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 925c958..d7a27b8 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"తక్కువ ప్రాధాన్యత నోటిఫికేషన్ చిహ్నాలను చూపించు"</string>
<string name="other" msgid="429768510980739978">"ఇతరం"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"టైల్ను తీసివేయండి"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ముగించడానికి టైల్ను జోడించండి"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"టైల్ను తరలించండి"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"టైల్ను జోడించండి"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>కు తరలించండి"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> స్థానానికి జోడించండి"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"స్థానం <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"శీఘ్ర సెట్టింగ్ల ఎడిటర్."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> నోటిఫికేషన్: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"సెట్టింగ్లను తెరవండి."</string>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 981a953..015ac90 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -45,4 +45,13 @@
<!-- Show a separate icon for low and high volume on the volume dialog -->
<bool name="config_showLowMediaVolumeIcon">true</bool>
+
+ <!-- Change the volume row tint when it is inactive, i.e. when it is being dismissed -->
+ <bool name="config_changeVolumeRowTintWhenInactive">false</bool>
+
+ <!-- The duraction of the show animation for the volume dialog in milliseconds -->
+ <integer name="config_dialogShowAnimationDurationMs">600</integer>
+
+ <!-- The duraction of the hide animation for the volume dialog in milliseconds -->
+ <integer name="config_dialogHideAnimationDurationMs">400</integer>
</resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index c613e9c..cb6089f 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"แสดงไอคอนการแจ้งเตือนลำดับความสำคัญต่ำ"</string>
<string name="other" msgid="429768510980739978">"อื่นๆ"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"นำชิ้นส่วนออก"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"เพิ่มชิ้นส่วนต่อท้าย"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ย้ายชิ้นส่วน"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"เพิ่มชิ้นส่วน"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ย้ายไปที่ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"เพิ่มไปยังตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"ตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"ตัวแก้ไขการตั้งค่าด่วน"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> การแจ้งเตือน: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"เปิดการตั้งค่า"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9411957..7e6aa62 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Ipakita ang mga icon ng notification na may mababang priority"</string>
<string name="other" msgid="429768510980739978">"Iba pa"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alisin ang tile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"idagdag ang tile sa dulo"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ilipat ang tile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Magdagdag ng tile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Ilipat sa <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Idagdag sa posisyong <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Posisyon <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor ng Mga mabilisang setting."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification sa <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Buksan ang mga setting."</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 4d46f93..dac352f 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Düşük öncelikli bildirim simgelerini göster"</string>
<string name="other" msgid="429768510980739978">"Diğer"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Karoyu kaldırmak için"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"Sona karo eklemek için"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Karoyu taşı"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Karo ekle"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna taşı"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna ekle"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Konum: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Hızlı ayar düzenleyicisi."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirimi: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Ayarları aç."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 399b53a..eeedac6 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string>
<string name="other" msgid="429768510980739978">"Інше"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити значок"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"додати значок у кінець"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити значок"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Додати значок"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Позиція <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Редактор швидких налаштувань."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Сповіщення <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Відкрити налаштування."</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 09e500d..4bd192f 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"کم ترجیحی اطلاع کے آئیکنز دکھائیں"</string>
<string name="other" msgid="429768510980739978">"دیگر"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ٹائل ہٹائیں"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ختم کرنے کے لیے ٹائل شامل کریں"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ٹائل منتقل کریں"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"ٹائل شامل کریں"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> میں منتقل کریں"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g> میں شامل کریں"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"فوری ترتیبات کا ایڈیٹر۔"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> اطلاع: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"ترتیبات کھولیں۔"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 61b91c7..7c3728a 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Muhim boʻlmagan bildirishnoma ikonkalarini koʻrsatish"</string>
<string name="other" msgid="429768510980739978">"Boshqa"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"katakchani olib tashlash"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"oxiriga katakcha kiritish"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Katakchani boshqa joyga olish"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Katakcha kiritish"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Bu joyga olish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bu joyga kiritish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Joylashuv: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Tezkor sozlamalar muharriri"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> bildirishnomasi: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Sozlamalarni ochish."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index dc2a5b8..f23f742 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Hiển thị biểu tượng thông báo có mức ưu tiên thấp"</string>
<string name="other" msgid="429768510980739978">"Khác"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"xóa ô"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"thêm ô vào cuối"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Di chuyển ô"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Thêm ô"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Di chuyển tới <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Thêm vào vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Trình chỉnh sửa cài đặt nhanh."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Thông báo của <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Mở phần cài đặt."</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 71a7d8e..9b81cb0 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"显示低优先级的通知图标"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除图块"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"将图块添加到末尾"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移动图块"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"添加图块"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"添加到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快捷设置编辑器。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g>通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"打开设置。"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 1d763d0..facb47a 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移圖塊"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"加圖塊"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯工具。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string>
@@ -975,7 +968,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式將會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"知道了"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"轉儲 SysUI 堆"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 303ddd9..4e4db70 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"將圖塊加到結尾處"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"新增圖塊"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"新增到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"快速設定編輯器。"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> 通知:<xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"開啟設定。"</string>
@@ -975,7 +968,7 @@
<string name="auto_saver_enabled_text" msgid="7889491183116752719">"省電模式會在電量低於 <xliff:g id="PERCENTAGE">%d</xliff:g>%% 時自動開啟。"</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"設定"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"我知道了"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"傾印 SysUI 記憶體快照"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
<string name="ongoing_privacy_chip_content_single_app" msgid="2969750601815230385">"「<xliff:g id="APP">%1$s</xliff:g>」正在使用<xliff:g id="TYPES_LIST">%2$s</xliff:g>。"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"有多個應用程式正在使用<xliff:g id="TYPES_LIST">%s</xliff:g>。"</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">"、 "</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index d8eb7ad..65e9a1c 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Bonisa izithonjana zesaziso zokubaluleka okuncane"</string>
<string name="other" msgid="429768510980739978">"Okunye"</string>
- <!-- no translation found for accessibility_qs_edit_remove_tile_action (775511891457193480) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_action (5051211910345301833) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_move (2009373939914517817) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_start_add (7560798153975555772) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_move_to_position (5198161544045930556) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_tile_add_to_position (9029163095148274690) -->
- <skip />
- <!-- no translation found for accessibility_qs_edit_position (4509277359815711830) -->
- <skip />
+ <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"susa ithayela"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"engeza ithayela ekugcineni"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hambisa ithayela"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Engeza ithayela"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hambisa ku-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engeza kusikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Isikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Isihleli sezilungiselelo ezisheshayo."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"<xliff:g id="ID_1">%1$s</xliff:g> isaziso: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Vula izilungiselelo."</string>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index cb49918..9b0ae1d 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -22,13 +22,15 @@
<color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
<color name="recents_tv_text_shadow_color">#7F000000</color>
- <!-- Background color for audio recording indicator (G800) -->
- <color name="tv_audio_recording_indicator_background">#FF3C4043</color>
<color name="tv_audio_recording_indicator_icon_background">#CC000000</color>
<color name="tv_audio_recording_indicator_stroke">#33FFFFFF</color>
<color name="red">#FFCC0000</color>
<color name="tv_volume_dialog_background">#E61F232B</color>
<color name="tv_volume_dialog_circle">#08FFFFFF</color>
+ <color name="tv_volume_dialog_seek_thumb_focus_ring">#1AFFFFFF</color>
+ <color name="tv_volume_dialog_seek_thumb_shadow">#40000000</color>
+ <color name="tv_volume_dialog_seek_bar_background">#A03C4043</color>
+ <color name="tv_volume_dialog_seek_bar_fill">#FFF8F9FA</color>
<color name="tv_volume_dialog_accent">#FFDADCE0</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ab09a96..d63724d 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -562,4 +562,13 @@
<!-- Show a separate icon for low and high volume on the volume dialog -->
<bool name="config_showLowMediaVolumeIcon">false</bool>
+
+ <!-- Change the volume row tint when it is inactive, i.e. when it is being dismissed -->
+ <bool name="config_changeVolumeRowTintWhenInactive">true</bool>
+
+ <!-- The duraction of the show animation for the volume dialog in milliseconds -->
+ <integer name="config_dialogShowAnimationDurationMs">300</integer>
+
+ <!-- The duraction of the hide animation for the volume dialog in milliseconds -->
+ <integer name="config_dialogHideAnimationDurationMs">250</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 98e8cde..48da5d9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -226,6 +226,8 @@
<dimen name="notification_guts_conversation_action_text_padding_start">32dp</dimen>
<dimen name="conversation_onboarding_bullet_gap_width">6dp</dimen>
+ <dimen name="notification_guts_header_top_padding">11dp</dimen>
+
<!-- The height of the header in inline settings -->
<dimen name="notification_guts_header_height">24dp</dimen>
@@ -482,20 +484,20 @@
<!-- The size of the gesture span needed to activate the "pull" notification expansion -->
<dimen name="pull_span_min">25dp</dimen>
- <dimen name="qs_tile_height">106dp</dimen>
+ <dimen name="qs_tile_height">96dp</dimen>
<!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 -->
<dimen name="qs_tile_layout_margin_side">18dp</dimen>
<dimen name="qs_tile_margin_horizontal">18dp</dimen>
<dimen name="qs_tile_margin_horizontal_two_line">2dp</dimen>
- <dimen name="qs_tile_margin_vertical">24dp</dimen>
+ <dimen name="qs_tile_margin_vertical">2dp</dimen>
<dimen name="qs_tile_margin_top_bottom">12dp</dimen>
<dimen name="qs_tile_margin_top_bottom_negative">-12dp</dimen>
<!-- The height of the qs customize header. Should be
- (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) -
+ (qs_panel_padding_top (48dp) + brightness_mirror_height (48dp) + qs_tile_margin_top (0dp)) -
(Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
-->
- <dimen name="qs_customize_header_min_height">46dp</dimen>
- <dimen name="qs_tile_margin_top">18dp</dimen>
+ <dimen name="qs_customize_header_min_height">28dp</dimen>
+ <dimen name="qs_tile_margin_top">0dp</dimen>
<dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen>
<dimen name="qs_tile_background_size">44dp</dimen>
<dimen name="qs_quick_tile_size">48dp</dimen>
@@ -1368,9 +1370,10 @@
<dimen name="config_rounded_mask_size_bottom">@*android:dimen/rounded_corner_radius_bottom</dimen>
<!-- Output switcher panel related dimensions -->
- <dimen name="media_output_dialog_padding_top">11dp</dimen>
+ <dimen name="media_output_dialog_list_margin">12dp</dimen>
<dimen name="media_output_dialog_list_max_height">364dp</dimen>
<dimen name="media_output_dialog_header_album_icon_size">52dp</dimen>
<dimen name="media_output_dialog_header_back_icon_size">36dp</dimen>
+ <dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
<dimen name="media_output_dialog_icon_corner_radius">16dp</dimen>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 8ed7929..c94bcaa 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -44,11 +44,6 @@
void startScreenPinning(int taskId) = 1;
/**
- * Notifies SystemUI that split screen has been invoked.
- */
- void onSplitScreenInvoked() = 5;
-
- /**
* Notifies SystemUI that Overview is shown.
*/
void onOverviewShown(boolean fromHome) = 6;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index 067ac9e..e5f2ad5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -33,7 +33,6 @@
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
public class WindowManagerWrapper {
@@ -75,7 +74,7 @@
WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
public static final int WINDOWING_MODE_MULTI_WINDOW =
WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
- public static final int WINDOWING_MODE_PINNED = WindowConfiguration.WINDOWING_MODE_PINNED;
+
public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY =
WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY =
@@ -84,13 +83,6 @@
private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
- /**
- * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive
- * updates from the window manager service.
- */
- private PinnedStackListenerForwarder mPinnedStackListenerForwarder =
- new PinnedStackListenerForwarder();
-
public static WindowManagerWrapper getInstance() {
return sInstance;
}
@@ -188,23 +180,6 @@
}
/**
- * Adds a pinned stack listener, which will receive updates from the window manager service
- * along with any other pinned stack listeners that were added via this method.
- */
- public void addPinnedStackListener(PinnedStackListener listener) throws RemoteException {
- mPinnedStackListenerForwarder.addListener(listener);
- WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
- DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
- }
-
- /**
- * Removes a pinned stack listener.
- */
- public void removePinnedStackListener(PinnedStackListener listener) {
- mPinnedStackListenerForwarder.removeListener(listener);
- }
-
- /**
* Mirrors a specified display. The SurfaceControl returned is the root of the mirrored
* hierarchy.
*
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 23195af..e99245f 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -33,13 +33,9 @@
import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.dagger.KeyguardBouncerScope;
-import com.android.systemui.dagger.qualifiers.Main;
import java.util.NoSuchElementException;
-import javax.inject.Inject;
-
/**
* Encapsulates all logic for secondary lockscreen state management.
*/
@@ -146,9 +142,9 @@
}
};
- private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent,
+ public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
- @Main Handler handler) {
+ Handler handler) {
mContext = context;
mHandler = handler;
mParent = parent;
@@ -238,26 +234,4 @@
getHolder().removeCallback(mSurfaceHolderCallback);
}
}
-
- @KeyguardBouncerScope
- public static class Factory {
- private final Context mContext;
- private final KeyguardSecurityContainer mParent;
- private final KeyguardUpdateMonitor mUpdateMonitor;
- private final Handler mHandler;
-
- @Inject
- public Factory(Context context, KeyguardSecurityContainer parent,
- KeyguardUpdateMonitor updateMonitor, @Main Handler handler) {
- mContext = context;
- mParent = parent;
- mUpdateMonitor = updateMonitor;
- mHandler = handler;
- }
-
- public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) {
- return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor,
- callback, mHandler);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index cc6df45..88f4176 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -16,26 +16,46 @@
package com.android.keyguard;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+
import android.content.Context;
+import android.content.res.ColorStateList;
+import android.os.AsyncTask;
+import android.os.CountDownTimer;
+import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
+import android.widget.LinearLayout;
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Base class for PIN and password unlock screens.
*/
-public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
+public abstract class KeyguardAbsKeyInputView extends LinearLayout
+ implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback {
+ protected KeyguardSecurityCallback mCallback;
+ protected LockPatternUtils mLockPatternUtils;
+ protected AsyncTask<?, ?, ?> mPendingLockCheck;
+ protected SecurityMessageDisplay mSecurityMessageDisplay;
protected View mEcaView;
protected boolean mEnableHaptics;
+ private boolean mDismissing;
+ protected boolean mResumed;
+ private CountDownTimer mCountdownTimer = null;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
- private KeyDownListener mKeyDownListener;
public KeyguardAbsKeyInputView(Context context) {
this(context, null);
@@ -43,10 +63,38 @@
public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
- void setEnableHaptics(boolean enableHaptics) {
- mEnableHaptics = enableHaptics;
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
+ }
+
+ @Override
+ public void reset() {
+ // start fresh
+ mDismissing = false;
+ resetPasswordText(false /* animate */, false /* announce */);
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (shouldLockout(deadline)) {
+ handleAttemptLockout(deadline);
+ } else {
+ resetState();
+ }
+ }
+
+ // Allow subclasses to override this behavior
+ protected boolean shouldLockout(long deadline) {
+ return deadline != 0;
}
protected abstract int getPasswordTextViewId();
@@ -54,7 +102,24 @@
@Override
protected void onFinishInflate() {
+ mLockPatternUtils = new LockPatternUtils(mContext);
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
+
+ EmergencyButton button = findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(this);
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
+ }
+
+ @Override
+ public void onEmergencyButtonClickedWhenInCall() {
+ mCallback.reset();
}
/*
@@ -66,14 +131,195 @@
return R.string.kg_wrong_password;
}
+ protected void verifyPasswordAndUnlock() {
+ if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
+
+ final LockscreenCredential password = getEnteredCredential();
+ setPasswordEntryInputEnabled(false);
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+ // to avoid accidental lockout, only count attempts that are long enough to be a
+ // real password. This may require some tweaking.
+ setPasswordEntryInputEnabled(true);
+ onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
+ password.zeroize();
+ return;
+ }
+
+ if (LatencyTracker.isEnabled(mContext)) {
+ LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
+ LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ password,
+ userId,
+ new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ if (LatencyTracker.isEnabled(mContext)) {
+ LatencyTracker.getInstance(mContext).onActionEnd(
+ ACTION_CHECK_CREDENTIAL);
+ }
+ onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPassword */);
+ password.zeroize();
+ }
+
+ @Override
+ public void onChecked(boolean matched, int timeoutMs) {
+ if (LatencyTracker.isEnabled(mContext)) {
+ LatencyTracker.getInstance(mContext).onActionEnd(
+ ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+ setPasswordEntryInputEnabled(true);
+ mPendingLockCheck = null;
+ if (!matched) {
+ onPasswordChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPassword */);
+ }
+ password.zeroize();
+ }
+
+ @Override
+ public void onCancelled() {
+ // We already got dismissed with the early matched callback, so we cancelled
+ // the check. However, we still need to note down the latency.
+ if (LatencyTracker.isEnabled(mContext)) {
+ LatencyTracker.getInstance(mContext).onActionEnd(
+ ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+ password.zeroize();
+ }
+ });
+ }
+
+ private void onPasswordChecked(int userId, boolean matched, int timeoutMs,
+ boolean isValidPassword) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ if (matched) {
+ mCallback.reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mDismissing = true;
+ mCallback.dismiss(true, userId);
+ }
+ } else {
+ if (isValidPassword) {
+ mCallback.reportUnlockAttempt(userId, false, timeoutMs);
+ if (timeoutMs > 0) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
+ userId, timeoutMs);
+ handleAttemptLockout(deadline);
+ }
+ }
+ if (timeoutMs == 0) {
+ mSecurityMessageDisplay.setMessage(getWrongPasswordStringId());
+ }
+ }
+ resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
+ }
+
protected abstract void resetPasswordText(boolean animate, boolean announce);
protected abstract LockscreenCredential getEnteredCredential();
protected abstract void setPasswordEntryEnabled(boolean enabled);
protected abstract void setPasswordEntryInputEnabled(boolean enabled);
+ // Prevent user from using the PIN/Password entry until scheduled deadline.
+ protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ setPasswordEntryEnabled(false);
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+ long secondsInFuture = (long) Math.ceil(
+ (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
+ mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
+ mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
+ R.plurals.kg_too_many_failed_attempts_countdown,
+ secondsRemaining, secondsRemaining));
+ }
+
+ @Override
+ public void onFinish() {
+ mSecurityMessageDisplay.setMessage("");
+ resetState();
+ }
+ }.start();
+ }
+
+ protected void onUserInput() {
+ if (mCallback != null) {
+ mCallback.userActivity();
+ mCallback.onUserInput();
+ }
+ mSecurityMessageDisplay.setMessage("");
+ }
+
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event);
+ // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
+ // We don't want to consider it valid user input because the UI
+ // will already respond to the event.
+ if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
+ onUserInput();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ mResumed = false;
+
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ mPendingLockCheck = null;
+ }
+ reset();
+ }
+
+ @Override
+ public void onResume(int reason) {
+ mResumed = true;
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ if (reason != PROMPT_REASON_NONE) {
+ int promtReasonStringRes = getPromptReasonStringRes(reason);
+ if (promtReasonStringRes != 0) {
+ mSecurityMessageDisplay.setMessage(promtReasonStringRes);
+ }
+ }
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (colorState != null) {
+ mSecurityMessageDisplay.setNextMessageColor(colorState);
+ }
+ mSecurityMessageDisplay.setMessage(message);
}
protected abstract int getPromptReasonStringRes(int reason);
@@ -87,12 +333,9 @@
}
}
- public void setKeyDownListener(KeyDownListener keyDownListener) {
- mKeyDownListener = keyDownListener;
- }
-
- public interface KeyDownListener {
- boolean onKeyDown(int keyCode, KeyEvent keyEvent);
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
deleted file mode 100644
index d957628..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
-import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;
-
-import android.content.res.ColorStateList;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
-import android.os.SystemClock;
-import android.view.KeyEvent;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockscreenCredential;
-import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
-import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
-
-public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
- extends KeyguardInputViewController<T> {
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final LockPatternUtils mLockPatternUtils;
- private final LatencyTracker mLatencyTracker;
- private CountDownTimer mCountdownTimer;
- protected KeyguardMessageAreaController mMessageAreaController;
- private boolean mDismissing;
- protected AsyncTask<?, ?, ?> mPendingLockCheck;
- protected boolean mResumed;
-
- private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
- // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
- // We don't want to consider it valid user input because the UI
- // will already respond to the event.
- if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
- onUserInput();
- }
- return false;
- };
-
- private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
- @Override
- public void onEmergencyButtonClickedWhenInCall() {
- getKeyguardSecurityCallback().reset();
- }
- };
-
- protected KeyguardAbsKeyInputViewController(T view,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- SecurityMode securityMode,
- LockPatternUtils lockPatternUtils,
- KeyguardSecurityCallback keyguardSecurityCallback,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- LatencyTracker latencyTracker) {
- super(view, securityMode, keyguardSecurityCallback);
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mLockPatternUtils = lockPatternUtils;
- mLatencyTracker = latencyTracker;
- KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
- mMessageAreaController = messageAreaControllerFactory.create(kma);
- }
-
- abstract void resetState();
-
- @Override
- public void init() {
- super.init();
- mMessageAreaController.init();
- }
-
- @Override
- protected void onViewAttached() {
- mView.setKeyDownListener(mKeyDownListener);
- mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
- EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(mEmergencyButtonCallback);
- }
- }
-
- @Override
- public void reset() {
- // start fresh
- mDismissing = false;
- mView.resetPasswordText(false /* animate */, false /* announce */);
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (shouldLockout(deadline)) {
- handleAttemptLockout(deadline);
- } else {
- mView.resetState();
- }
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (colorState != null) {
- mMessageAreaController.setNextMessageColor(colorState);
- }
- mMessageAreaController.setMessage(message);
- }
-
- // Allow subclasses to override this behavior
- protected boolean shouldLockout(long deadline) {
- return deadline != 0;
- }
-
- // Prevent user from using the PIN/Password entry until scheduled deadline.
- protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mView.setPasswordEntryEnabled(false);
- long elapsedRealtime = SystemClock.elapsedRealtime();
- long secondsInFuture = (long) Math.ceil(
- (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
- mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
- mMessageAreaController.setMessage(mView.getResources().getQuantityString(
- R.plurals.kg_too_many_failed_attempts_countdown,
- secondsRemaining, secondsRemaining));
- }
-
- @Override
- public void onFinish() {
- mMessageAreaController.setMessage("");
- resetState();
- }
- }.start();
- }
-
- void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
- if (matched) {
- getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
- if (dismissKeyguard) {
- mDismissing = true;
- getKeyguardSecurityCallback().dismiss(true, userId);
- }
- } else {
- if (isValidPassword) {
- getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
- if (timeoutMs > 0) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- userId, timeoutMs);
- handleAttemptLockout(deadline);
- }
- }
- if (timeoutMs == 0) {
- mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
- }
- }
- mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
- }
-
- protected void verifyPasswordAndUnlock() {
- if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
-
- final LockscreenCredential password = mView.getEnteredCredential();
- mView.setPasswordEntryInputEnabled(false);
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- }
-
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
- // to avoid accidental lockout, only count attempts that are long enough to be a
- // real password. This may require some tweaking.
- mView.setPasswordEntryInputEnabled(true);
- onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
- password.zeroize();
- return;
- }
-
- mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
- mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
-
- mKeyguardUpdateMonitor.setCredentialAttempted();
- mPendingLockCheck = LockPatternChecker.checkCredential(
- mLockPatternUtils,
- password,
- userId,
- new LockPatternChecker.OnCheckCallback() {
-
- @Override
- public void onEarlyMatched() {
- mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
-
- onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
- true /* isValidPassword */);
- password.zeroize();
- }
-
- @Override
- public void onChecked(boolean matched, int timeoutMs) {
- mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- mView.setPasswordEntryInputEnabled(true);
- mPendingLockCheck = null;
- if (!matched) {
- onPasswordChecked(userId, false /* matched */, timeoutMs,
- true /* isValidPassword */);
- }
- password.zeroize();
- }
-
- @Override
- public void onCancelled() {
- // We already got dismissed with the early matched callback, so we cancelled
- // the check. However, we still need to note down the latency.
- mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- password.zeroize();
- }
- });
- }
-
- @Override
- public void showPromptReason(int reason) {
- if (reason != PROMPT_REASON_NONE) {
- int promtReasonStringRes = mView.getPromptReasonStringRes(reason);
- if (promtReasonStringRes != 0) {
- mMessageAreaController.setMessage(promtReasonStringRes);
- }
- }
- }
-
- protected void onUserInput() {
- getKeyguardSecurityCallback().userActivity();
- getKeyguardSecurityCallback().onUserInput();
- mMessageAreaController.setMessage("");
- }
-
- @Override
- public void onResume(int reason) {
- mResumed = true;
- }
-
- @Override
- public void onPause() {
- mResumed = false;
-
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- mPendingLockCheck = null;
- }
- reset();
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 36d5543..be21d20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -39,6 +39,7 @@
import com.android.systemui.R;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
+import com.android.systemui.util.InjectionInflationController;
import javax.inject.Inject;
@@ -48,6 +49,7 @@
private final MediaRouter mMediaRouter;
private final DisplayManager mDisplayService;
+ private final InjectionInflationController mInjectableInflater;
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
private final Context mContext;
@@ -90,8 +92,10 @@
@Inject
public KeyguardDisplayManager(Context context,
+ InjectionInflationController injectableInflater,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
mContext = context;
+ mInjectableInflater = injectableInflater;
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
mMediaRouter = mContext.getSystemService(MediaRouter.class);
mDisplayService = mContext.getSystemService(DisplayManager.class);
@@ -127,7 +131,8 @@
Presentation presentation = mPresentations.get(displayId);
if (presentation == null) {
final Presentation newPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext));
+ mKeyguardStatusViewComponentFactory,
+ mInjectableInflater.injectable(LayoutInflater.from(mContext)));
newPresentation.setOnDismissListener(dialog -> {
if (newPresentation.equals(mPresentations.get(displayId))) {
mPresentations.remove(displayId);
@@ -245,7 +250,7 @@
private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private final LayoutInflater mLayoutInflater;
+ private final LayoutInflater mInjectableLayoutInflater;
private KeyguardClockSwitchController mKeyguardClockSwitchController;
private View mClock;
private int mUsableWidth;
@@ -265,10 +270,10 @@
KeyguardPresentation(Context context, Display display,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
- LayoutInflater layoutInflater) {
+ LayoutInflater injectionLayoutInflater) {
super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- mLayoutInflater = layoutInflater;
+ mInjectableLayoutInflater = injectionLayoutInflater;
getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
setCancelable(false);
}
@@ -294,7 +299,7 @@
mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
- setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null));
+ setContentView(mInjectableLayoutInflater.inflate(R.layout.keyguard_presentation, null));
// Logic to make the lock screen fullscreen
getWindow().getDecorView().setSystemUiVisibility(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 9ffa658..7aabb17 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -178,18 +178,18 @@
/** Initialize the Controller. */
public void init() {
super.init();
+ mView.setViewMediatorCallback(mViewMediatorCallback);
+ // Update ViewMediator with the current input method requirements
+ mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardSecurityContainerController.init();
+ mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
+ mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
protected void onViewAttached() {
- mView.setViewMediatorCallback(mViewMediatorCallback);
- // Update ViewMediator with the current input method requirements
- mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput());
mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
mView.setOnKeyListener(mOnKeyListener);
- mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
- mKeyguardSecurityContainerController.showPrimarySecurityScreen(false);
}
@Override
@@ -350,7 +350,7 @@
}
public boolean handleBackKey() {
- if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
+ if (mKeyguardSecurityContainerController.getCurrentSecuritySelection()
!= SecurityMode.None) {
mKeyguardSecurityContainerController.dismiss(
false, KeyguardUpdateMonitor.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
deleted file mode 100644
index d42a53c..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.LinearLayout;
-
-import androidx.annotation.Nullable;
-
-/**
- * A Base class for all Keyguard password/pattern/pin related inputs.
- */
-public abstract class KeyguardInputView extends LinearLayout {
-
- public KeyguardInputView(Context context) {
- super(context);
- }
-
- public KeyguardInputView(Context context,
- @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- public KeyguardInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- abstract CharSequence getTitle();
-
- boolean disallowInterceptTouch(MotionEvent event) {
- return false;
- }
-
- void startAppearAnimation() {}
-
- boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
deleted file mode 100644
index fbda818..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.telephony.TelephonyManager;
-import android.view.inputmethod.InputMethodManager;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.ViewController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import javax.inject.Inject;
-
-
-/** Controller for a {@link KeyguardSecurityView}. */
-public abstract class KeyguardInputViewController<T extends KeyguardInputView>
- extends ViewController<T> implements KeyguardSecurityView {
-
- private final SecurityMode mSecurityMode;
- private final KeyguardSecurityCallback mKeyguardSecurityCallback;
- private boolean mPaused;
-
-
- // The following is used to ignore callbacks from SecurityViews that are no longer current
- // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
- // state for the current security method.
- private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
- @Override
- public void userActivity() { }
- @Override
- public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
- @Override
- public boolean isVerifyUnlockOnly() {
- return false;
- }
- @Override
- public void dismiss(boolean securityVerified, int targetUserId) { }
- @Override
- public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) { }
- @Override
- public void onUserInput() { }
- @Override
- public void reset() {}
- };
-
- protected KeyguardInputViewController(T view, SecurityMode securityMode,
- KeyguardSecurityCallback keyguardSecurityCallback) {
- super(view);
- mSecurityMode = securityMode;
- mKeyguardSecurityCallback = keyguardSecurityCallback;
- }
-
- @Override
- protected void onViewAttached() {
- }
-
- @Override
- protected void onViewDetached() {
- }
-
- SecurityMode getSecurityMode() {
- return mSecurityMode;
- }
-
- protected KeyguardSecurityCallback getKeyguardSecurityCallback() {
- if (mPaused) {
- return mNullCallback;
- }
-
- return mKeyguardSecurityCallback;
- }
-
- @Override
- public void reset() {
- }
-
- @Override
- public void onPause() {
- mPaused = true;
- }
-
- @Override
- public void onResume(int reason) {
- mPaused = false;
- }
-
- @Override
- public void showPromptReason(int reason) {
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- }
-
- public void startAppearAnimation() {
- mView.startAppearAnimation();
- }
-
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return mView.startDisappearAnimation(finishRunnable);
- }
-
- @Override
- public CharSequence getTitle() {
- return mView.getTitle();
- }
-
- /** Finds the index of this view in the suppplied parent view. */
- public int getIndexIn(KeyguardSecurityViewFlipper view) {
- return view.indexOfChild(mView);
- }
-
- /** Factory for a {@link KeyguardInputViewController}. */
- public static class Factory {
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final LockPatternUtils mLockPatternUtils;
- private final LatencyTracker mLatencyTracker;
- private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
- private final InputMethodManager mInputMethodManager;
- private final DelayableExecutor mMainExecutor;
- private final Resources mResources;
- private LiftToActivateListener mLiftToActivateListener;
- private TelephonyManager mTelephonyManager;
-
- @Inject
- public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
- LockPatternUtils lockPatternUtils,
- LatencyTracker latencyTracker,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
- @Main Resources resources, LiftToActivateListener liftToActivateListener,
- TelephonyManager telephonyManager) {
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mLockPatternUtils = lockPatternUtils;
- mLatencyTracker = latencyTracker;
- mMessageAreaControllerFactory = messageAreaControllerFactory;
- mInputMethodManager = inputMethodManager;
- mMainExecutor = mainExecutor;
- mResources = resources;
- mLiftToActivateListener = liftToActivateListener;
- mTelephonyManager = telephonyManager;
- }
-
- /** Create a new {@link KeyguardInputViewController}. */
- public KeyguardInputViewController create(KeyguardInputView keyguardInputView,
- SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) {
- if (keyguardInputView instanceof KeyguardPatternView) {
- return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView,
- mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
- keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory);
- } else if (keyguardInputView instanceof KeyguardPasswordView) {
- return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView,
- mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
- keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mInputMethodManager, mMainExecutor, mResources);
- } else if (keyguardInputView instanceof KeyguardPINView) {
- return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
- mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
- keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener);
- } else if (keyguardInputView instanceof KeyguardSimPinView) {
- return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
- mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
- keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, mTelephonyManager);
- } else if (keyguardInputView instanceof KeyguardSimPukView) {
- return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
- mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
- keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
- mLiftToActivateListener, mTelephonyManager);
- }
-
- throw new RuntimeException("Unable to find controller for " + keyguardInputView);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
index 1a0a437..a8b1451 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
+
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -29,14 +31,20 @@
import android.view.View;
import android.widget.TextView;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import java.lang.ref.WeakReference;
+import javax.inject.Inject;
+import javax.inject.Named;
+
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
-public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
+public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay,
+ ConfigurationController.ConfigurationListener {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
@@ -48,26 +56,71 @@
private static final int DEFAULT_COLOR = -1;
private final Handler mHandler;
+ private final ConfigurationController mConfigurationController;
private ColorStateList mDefaultColorState;
private CharSequence mMessage;
private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
private boolean mBouncerVisible;
- public KeyguardMessageArea(Context context, AttributeSet attrs) {
+ private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
+ public void onFinishedGoingToSleep(int why) {
+ setSelected(false);
+ }
+
+ public void onStartedWakingUp() {
+ setSelected(true);
+ }
+
+ @Override
+ public void onKeyguardBouncerChanged(boolean bouncer) {
+ mBouncerVisible = bouncer;
+ update();
+ }
+ };
+
+ public KeyguardMessageArea(Context context) {
+ super(context, null);
+ throw new IllegalStateException("This constructor should never be invoked");
+ }
+
+ @Inject
+ public KeyguardMessageArea(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
+ ConfigurationController configurationController) {
+ this(context, attrs, Dependency.get(KeyguardUpdateMonitor.class), configurationController);
+ }
+
+ public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor,
+ ConfigurationController configurationController) {
super(context, attrs);
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
+ monitor.registerCallback(mInfoCallback);
mHandler = new Handler(Looper.myLooper());
+ mConfigurationController = configurationController;
onThemeChanged();
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mConfigurationController.addCallback(this);
+ onThemeChanged();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mConfigurationController.removeCallback(this);
+ }
+
+ @Override
public void setNextMessageColor(ColorStateList colorState) {
mNextMessageColorState = colorState;
}
- void onThemeChanged() {
+ @Override
+ public void onThemeChanged() {
TypedArray array = mContext.obtainStyledAttributes(new int[] {
R.attr.wallpaperTextColor
});
@@ -77,7 +130,8 @@
update();
}
- void onDensityOrFontScaleChanged() {
+ @Override
+ public void onDensityOrFontScaleChanged() {
TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] {
android.R.attr.textSize
});
@@ -123,6 +177,12 @@
return messageArea;
}
+ @Override
+ protected void onFinishInflate() {
+ boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive();
+ setSelected(shouldMarquee); // This is required to ensure marquee works
+ }
+
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
@@ -136,7 +196,7 @@
update();
}
- void update() {
+ private void update() {
CharSequence status = mMessage;
setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE);
setText(status);
@@ -148,9 +208,6 @@
setTextColor(colorState);
}
- public void setBouncerVisible(boolean bouncerVisible) {
- mBouncerVisible = bouncerVisible;
- }
/**
* Runnable used to delay accessibility announcements.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 1618e8e..f056bdb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -16,10 +16,7 @@
package com.android.keyguard;
-import android.content.res.ColorStateList;
-
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -29,35 +26,6 @@
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
-
- private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
- public void onFinishedGoingToSleep(int why) {
- mView.setSelected(false);
- }
-
- public void onStartedWakingUp() {
- mView.setSelected(true);
- }
-
- @Override
- public void onKeyguardBouncerChanged(boolean bouncer) {
- mView.setBouncerVisible(bouncer);
- mView.update();
- }
- };
-
- private ConfigurationListener mConfigurationListener = new ConfigurationListener() {
- @Override
- public void onThemeChanged() {
- mView.onThemeChanged();
- }
-
- @Override
- public void onDensityOrFontScaleChanged() {
- mView.onDensityOrFontScaleChanged();
- }
- };
-
private KeyguardMessageAreaController(KeyguardMessageArea view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController) {
@@ -69,31 +37,17 @@
@Override
protected void onViewAttached() {
- mConfigurationController.addCallback(mConfigurationListener);
- mKeyguardUpdateMonitor.registerCallback(mInfoCallback);
- mView.setSelected(mKeyguardUpdateMonitor.isDeviceInteractive());
- mView.onThemeChanged();
+ //mConfigurationController.addCallback();
+ //mKeyguardUpdateMonitor.registerCallback();
}
@Override
protected void onViewDetached() {
- mConfigurationController.removeCallback(mConfigurationListener);
- mKeyguardUpdateMonitor.removeCallback(mInfoCallback);
+ //mConfigurationController.removeCallback();
+ //mKeyguardUpdateMonitor.removeCallback();
}
- public void setMessage(CharSequence s) {
- mView.setMessage(s);
- }
-
- public void setMessage(int resId) {
- mView.setMessage(resId);
- }
-
- public void setNextMessageColor(ColorStateList colorState) {
- mView.setNextMessageColor(colorState);
- }
-
- /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */
+ /** Factory for createing {@link com.android.keyguard.KeyguardMessageAreaController}. */
public static class Factory {
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 580d704..12ea1d5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -24,6 +24,7 @@
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
@@ -39,8 +40,10 @@
private ViewGroup mRow1;
private ViewGroup mRow2;
private ViewGroup mRow3;
+ private View mDivider;
private int mDisappearYTranslation;
private View[][] mViews;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
public KeyguardPINView(Context context) {
this(context, null);
@@ -60,10 +63,15 @@
mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
+ mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
}
@Override
protected void resetState() {
+ super.resetState();
+ if (mSecurityMessageDisplay != null) {
+ mSecurityMessageDisplay.setMessage("");
+ }
}
@Override
@@ -80,6 +88,7 @@
mRow1 = findViewById(R.id.row1);
mRow2 = findViewById(R.id.row2);
mRow3 = findViewById(R.id.row3);
+ mDivider = findViewById(R.id.divider);
mViews = new View[][]{
new View[]{
mRow0, null, null
@@ -103,6 +112,18 @@
new View[]{
null, mEcaView, null
}};
+
+ View cancelBtn = findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ mCallback.reset();
+ mCallback.onCancelClicked();
+ });
+ }
+ }
+
+ @Override
+ public void showUsabilityHint() {
}
@Override
@@ -126,21 +147,24 @@
});
}
- public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
- final Runnable finishRunnable) {
-
+ @Override
+ public boolean startDisappearAnimation(final Runnable finishRunnable) {
enableClipping(false);
setTranslationY(0);
AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */,
mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator());
- DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
+ DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
+ .needsSlowUnlockTransition()
? mDisappearAnimationUtilsLocked
: mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mViews,
- () -> {
- enableClipping(true);
- if (finishRunnable != null) {
- finishRunnable.run();
+ new Runnable() {
+ @Override
+ public void run() {
+ enableClipping(true);
+ if (finishRunnable != null) {
+ finishRunnable.run();
+ }
}
});
return true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index aaa5efe..97317cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -16,37 +16,50 @@
package com.android.keyguard;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
-
import android.content.Context;
import android.graphics.Rect;
+import android.os.UserHandle;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.method.TextKeyListener;
import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.TextViewInputDisabler;
import com.android.systemui.R;
+
+import java.util.List;
/**
* Displays an alphanumeric (latin-1) key entry for the user to enter
* an unlock password
*/
-public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
+public class KeyguardPasswordView extends KeyguardAbsKeyInputView
+ implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
+ private final boolean mShowImeAtScreenOn;
private final int mDisappearYTranslation;
// A delay constant to be used in a workaround for the situation where InputMethodManagerService
// is not switched to the new user yet.
// TODO: Remove this by ensuring such a race condition never happens.
+ private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
+ InputMethodManager mImm;
private TextView mPasswordEntry;
private TextViewInputDisabler mPasswordEntryDisabler;
+ private View mSwitchImeButton;
private Interpolator mLinearOutSlowInInterpolator;
private Interpolator mFastOutLinearInInterpolator;
@@ -57,6 +70,8 @@
public KeyguardPasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mShowImeAtScreenOn = context.getResources().
+ getBoolean(R.bool.kg_show_ime_at_screen_on);
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(
@@ -67,6 +82,20 @@
@Override
protected void resetState() {
+ mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
+ if (mSecurityMessageDisplay != null) {
+ mSecurityMessageDisplay.setMessage("");
+ }
+ final boolean wasDisabled = mPasswordEntry.isEnabled();
+ setPasswordEntryEnabled(true);
+ setPasswordEntryInputEnabled(true);
+ // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
+ if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
+ return;
+ }
+ if (wasDisabled) {
+ mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
}
@Override
@@ -75,6 +104,29 @@
}
@Override
+ public boolean needsInput() {
+ return true;
+ }
+
+ @Override
+ public void onResume(final int reason) {
+ super.onResume(reason);
+
+ // Wait a bit to focus the field so the focusable flag on the window is already set then.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (isShown() && mPasswordEntry.isEnabled()) {
+ mPasswordEntry.requestFocus();
+ if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
+ mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
protected int getPromptReasonStringRes(int reason) {
switch (reason) {
case PROMPT_REASON_RESTART:
@@ -94,13 +146,97 @@
}
}
+ @Override
+ public void onPause() {
+ super.onPause();
+ mImm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+
+ @Override
+ public void onStartingToHide() {
+ mImm.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+
+ private void updateSwitchImeButton() {
+ // If there's more than one IME, enable the IME switcher button
+ final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
+ final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false);
+ if (wasVisible != shouldBeVisible) {
+ mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
+ }
+
+ // TODO: Check if we still need this hack.
+ // If no icon is visible, reset the start margin on the password field so the text is
+ // still centered.
+ if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
+ android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
+ if (params instanceof MarginLayoutParams) {
+ final MarginLayoutParams mlp = (MarginLayoutParams) params;
+ mlp.setMarginStart(0);
+ mPasswordEntry.setLayoutParams(params);
+ }
+ }
+ }
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mImm = (InputMethodManager) getContext().getSystemService(
+ Context.INPUT_METHOD_SERVICE);
+
mPasswordEntry = findViewById(getPasswordTextViewId());
+ mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry);
+ mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
+ mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
+ | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ mPasswordEntry.setOnEditorActionListener(this);
+ mPasswordEntry.addTextChangedListener(this);
+
+ // Poke the wakelock any time the text is selected or modified
+ mPasswordEntry.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCallback.userActivity();
+ }
+ });
+
+ // Set selected property on so the view can send accessibility events.
+ mPasswordEntry.setSelected(true);
+
+ mSwitchImeButton = findViewById(R.id.switch_ime_button);
+ mSwitchImeButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCallback.userActivity(); // Leave the screen on a bit longer
+ // Do not show auxiliary subtypes in password lock screen.
+ mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */,
+ getContext().getDisplayId());
+ }
+ });
+
+ View cancelBtn = findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ mCallback.reset();
+ mCallback.onCancelClicked();
+ });
+ }
+
+ // If there's more than one IME, enable the IME switcher button
+ updateSwitchImeButton();
+
+ // When we the current user is switching, InputMethodManagerService sometimes has not
+ // switched internal state yet here. As a quick workaround, we check the keyboard state
+ // again.
+ // TODO: Remove this workaround by ensuring such a race condition never happens.
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ updateSwitchImeButton();
+ }
+ }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
}
@Override
@@ -129,6 +265,59 @@
mPasswordEntryDisabler.setInputEnabled(enabled);
}
+ /**
+ * Method adapted from com.android.inputmethod.latin.Utils
+ *
+ * @param imm The input method manager
+ * @param shouldIncludeAuxiliarySubtypes
+ * @return true if we have multiple IMEs to choose from
+ */
+ private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
+ final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> enabledImis =
+ imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
+
+ // Number of the filtered IMEs
+ int filteredImisCount = 0;
+
+ for (InputMethodInfo imi : enabledImis) {
+ // We can return true immediately after we find two or more filtered IMEs.
+ if (filteredImisCount > 1) return true;
+ final List<InputMethodSubtype> subtypes =
+ imm.getEnabledInputMethodSubtypeList(imi, true);
+ // IMEs that have no subtypes should be counted.
+ if (subtypes.isEmpty()) {
+ ++filteredImisCount;
+ continue;
+ }
+
+ int auxCount = 0;
+ for (InputMethodSubtype subtype : subtypes) {
+ if (subtype.isAuxiliary()) {
+ ++auxCount;
+ }
+ }
+ final int nonAuxCount = subtypes.size() - auxCount;
+
+ // IMEs that have one or more non-auxiliary subtypes should be counted.
+ // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+ // subtypes should be counted as well.
+ if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+ ++filteredImisCount;
+ continue;
+ }
+ }
+
+ return filteredImisCount > 1
+ // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled
+ // input method subtype (The current IME should be LatinIME.)
+ || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
@Override
public int getWrongPasswordStringId() {
return R.string.kg_wrong_password;
@@ -157,8 +346,45 @@
}
@Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ if (mCallback != null) {
+ mCallback.userActivity();
+ }
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // Poor man's user edit detection, assuming empty text is programmatic and everything else
+ // is from the user.
+ if (!TextUtils.isEmpty(s)) {
+ onUserInput();
+ }
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Check if this was the result of hitting the enter key
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_NULL
+ || actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT);
+ final boolean isKeyboardEnterKey = event != null
+ && KeyEvent.isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ verifyPasswordAndUnlock();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public CharSequence getTitle() {
- return getResources().getString(
+ return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_password_unlock);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
deleted file mode 100644
index d34ea8c..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.content.res.Resources;
-import android.os.UserHandle;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.method.TextKeyListener;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup.MarginLayoutParams;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-import android.widget.TextView;
-import android.widget.TextView.OnEditorActionListener;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import java.util.List;
-
-public class KeyguardPasswordViewController
- extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> {
-
- private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms
-
- private final KeyguardSecurityCallback mKeyguardSecurityCallback;
- private final InputMethodManager mInputMethodManager;
- private final DelayableExecutor mMainExecutor;
- private final boolean mShowImeAtScreenOn;
- private TextView mPasswordEntry;
- private View mSwitchImeButton;
-
- private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> {
- // Check if this was the result of hitting the enter key
- final boolean isSoftImeEvent = event == null
- && (actionId == EditorInfo.IME_NULL
- || actionId == EditorInfo.IME_ACTION_DONE
- || actionId == EditorInfo.IME_ACTION_NEXT);
- final boolean isKeyboardEnterKey = event != null
- && KeyEvent.isConfirmKey(event.getKeyCode())
- && event.getAction() == KeyEvent.ACTION_DOWN;
- if (isSoftImeEvent || isKeyboardEnterKey) {
- verifyPasswordAndUnlock();
- return true;
- }
- return false;
- };
-
- private final TextWatcher mTextWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- mKeyguardSecurityCallback.userActivity();
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- if (!TextUtils.isEmpty(s)) {
- onUserInput();
- }
- }
- };
-
- protected KeyguardPasswordViewController(KeyguardPasswordView view,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- SecurityMode securityMode,
- LockPatternUtils lockPatternUtils,
- KeyguardSecurityCallback keyguardSecurityCallback,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- LatencyTracker latencyTracker,
- InputMethodManager inputMethodManager,
- @Main DelayableExecutor mainExecutor,
- @Main Resources resources) {
- super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
- messageAreaControllerFactory, latencyTracker);
- mKeyguardSecurityCallback = keyguardSecurityCallback;
- mInputMethodManager = inputMethodManager;
- mMainExecutor = mainExecutor;
- mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on);
- mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
- mSwitchImeButton = mView.findViewById(R.id.switch_ime_button);
- }
-
- @Override
- protected void onViewAttached() {
- super.onViewAttached();
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
- mPasswordEntry.setKeyListener(TextKeyListener.getInstance());
- mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT
- | InputType.TYPE_TEXT_VARIATION_PASSWORD);
-
- // Set selected property on so the view can send accessibility events.
- mPasswordEntry.setSelected(true);
- mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener);
- mPasswordEntry.addTextChangedListener(mTextWatcher);
- // Poke the wakelock any time the text is selected or modified
- mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity());
-
- mSwitchImeButton.setOnClickListener(v -> {
- mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer
- // Do not show auxiliary subtypes in password lock screen.
- mInputMethodManager.showInputMethodPickerFromSystem(false,
- mView.getContext().getDisplayId());
- });
-
- View cancelBtn = mView.findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- mKeyguardSecurityCallback.reset();
- mKeyguardSecurityCallback.onCancelClicked();
- });
- }
-
- // If there's more than one IME, enable the IME switcher button
- updateSwitchImeButton();
-
- // When we the current user is switching, InputMethodManagerService sometimes has not
- // switched internal state yet here. As a quick workaround, we check the keyboard state
- // again.
- // TODO: Remove this workaround by ensuring such a race condition never happens.
- mMainExecutor.executeDelayed(
- this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON);
- }
-
- @Override
- protected void onViewDetached() {
- super.onViewDetached();
- mPasswordEntry.setOnEditorActionListener(null);
- }
-
- @Override
- public boolean needsInput() {
- return true;
- }
-
- @Override
- void resetState() {
- mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser()));
- mMessageAreaController.setMessage("");
- final boolean wasDisabled = mPasswordEntry.isEnabled();
- mView.setPasswordEntryEnabled(true);
- mView.setPasswordEntryInputEnabled(true);
- // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage.
- if (!mResumed || !mPasswordEntry.isVisibleToUser()) {
- return;
- }
- if (wasDisabled) {
- mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
- }
-
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- // Wait a bit to focus the field so the focusable flag on the window is already set then.
- mMainExecutor.execute(() -> {
- if (mView.isShown() && mPasswordEntry.isEnabled()) {
- mPasswordEntry.requestFocus();
- if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
- mInputMethodManager.showSoftInput(
- mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
- }
- }
- });
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
- }
-
- @Override
- public void onStartingToHide() {
- mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0);
- }
-
- private void updateSwitchImeButton() {
- // If there's more than one IME, enable the IME switcher button
- final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE;
- final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(
- mInputMethodManager, false);
- if (wasVisible != shouldBeVisible) {
- mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE);
- }
-
- // TODO: Check if we still need this hack.
- // If no icon is visible, reset the start margin on the password field so the text is
- // still centered.
- if (mSwitchImeButton.getVisibility() != View.VISIBLE) {
- android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams();
- if (params instanceof MarginLayoutParams) {
- final MarginLayoutParams mlp = (MarginLayoutParams) params;
- mlp.setMarginStart(0);
- mPasswordEntry.setLayoutParams(params);
- }
- }
- }
-
- /**
- * Method adapted from com.android.inputmethod.latin.Utils
- *
- * @param imm The input method manager
- * @param shouldIncludeAuxiliarySubtypes
- * @return true if we have multiple IMEs to choose from
- */
- private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm,
- final boolean shouldIncludeAuxiliarySubtypes) {
- final List<InputMethodInfo> enabledImis =
- imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser());
-
- // Number of the filtered IMEs
- int filteredImisCount = 0;
-
- for (InputMethodInfo imi : enabledImis) {
- // We can return true immediately after we find two or more filtered IMEs.
- if (filteredImisCount > 1) return true;
- final List<InputMethodSubtype> subtypes =
- imm.getEnabledInputMethodSubtypeList(imi, true);
- // IMEs that have no subtypes should be counted.
- if (subtypes.isEmpty()) {
- ++filteredImisCount;
- continue;
- }
-
- int auxCount = 0;
- for (InputMethodSubtype subtype : subtypes) {
- if (subtype.isAuxiliary()) {
- ++auxCount;
- }
- }
- final int nonAuxCount = subtypes.size() - auxCount;
-
- // IMEs that have one or more non-auxiliary subtypes should be counted.
- // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
- // subtypes should be counted as well.
- if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
- ++filteredImisCount;
- continue;
- }
- }
-
- return filteredImisCount > 1
- // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's
- //enabled input method subtype (The current IME should be LatinIME.)
- || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index bdcf467..c4a9fcb 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,39 +15,62 @@
*/
package com.android.keyguard;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+
import android.content.Context;
+import android.content.res.ColorStateList;
import android.graphics.Rect;
+import android.os.AsyncTask;
+import android.os.CountDownTimer;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.widget.LinearLayout;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.LatencyTracker;
+import com.android.internal.widget.LockPatternChecker;
+import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
-public class KeyguardPatternView extends KeyguardInputView
- implements AppearAnimationCreator<LockPatternView.CellState> {
+import java.util.List;
+
+public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
+ AppearAnimationCreator<LockPatternView.CellState>,
+ EmergencyButton.EmergencyButtonCallback {
private static final String TAG = "SecurityPatternView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ // how long before we clear the wrong pattern
+ private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
// how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK
private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000;
+ // how many cells the user has to cross before we poke the wakelock
+ private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
+
// How much we scale up the duration of the disappear animation when the current user is locked
public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f;
// Extra padding, in pixels, that should eat touch events.
private static final int PATTERNS_TOUCH_AREA_EXTENSION = 40;
+ private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final AppearAnimationUtils mAppearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtils;
private final DisappearAnimationUtils mDisappearAnimationUtilsLocked;
@@ -55,7 +78,11 @@
private final Rect mTempRect = new Rect();
private final Rect mLockPatternScreenBounds = new Rect();
+ private CountDownTimer mCountdownTimer = null;
+ private LockPatternUtils mLockPatternUtils;
+ private AsyncTask<?, ?, ?> mPendingLockCheck;
private LockPatternView mLockPatternView;
+ private KeyguardSecurityCallback mCallback;
/**
* Keeps track of the last time we poked the wake lock during dispatching of the touch event.
@@ -65,9 +92,26 @@
*/
private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS;
+ /**
+ * Useful for clearing out the wrong pattern after a delay
+ */
+ private Runnable mCancelPatternRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLockPatternView.clearPattern();
+ }
+ };
+ @VisibleForTesting
KeyguardMessageArea mSecurityMessageDisplay;
private View mEcaView;
private ViewGroup mContainer;
+ private int mDisappearYTranslation;
+
+ enum FooterMode {
+ Normal,
+ ForgotLockPattern,
+ VerifyUnlocked
+ }
public KeyguardPatternView(Context context) {
this(context, null);
@@ -75,6 +119,7 @@
public KeyguardPatternView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mAppearAnimationUtils = new AppearAnimationUtils(context,
AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* translationScale */,
2.0f /* delayScale */, AnimationUtils.loadInterpolator(
@@ -87,16 +132,50 @@
(long) (125 * DISAPPEAR_MULTIPLIER_LOCKED), 1.2f /* translationScale */,
0.6f /* delayScale */, AnimationUtils.loadInterpolator(
mContext, android.R.interpolator.fast_out_linear_in));
+ mDisappearYTranslation = getResources().getDimensionPixelSize(
+ R.dimen.disappear_y_translation);
+ }
+
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mLockPatternUtils = mLockPatternUtils == null
+ ? new LockPatternUtils(mContext) : mLockPatternUtils;
mLockPatternView = findViewById(R.id.lockPatternView);
+ mLockPatternView.setSaveEnabled(false);
+ mLockPatternView.setOnPatternListener(new UnlockPatternListener());
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
+
+ // vibrate mode will be the same for the life of this screen
+ mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
mEcaView = findViewById(R.id.keyguard_selector_fade_container);
mContainer = findViewById(R.id.container);
+
+ EmergencyButton button = findViewById(R.id.emergency_call_button);
+ if (button != null) {
+ button.setCallback(this);
+ }
+
+ View cancelBtn = findViewById(R.id.cancel_button);
+ if (cancelBtn != null) {
+ cancelBtn.setOnClickListener(view -> {
+ mCallback.reset();
+ mCallback.onCancelClicked();
+ });
+ }
}
@Override
@@ -106,6 +185,11 @@
}
@Override
+ public void onEmergencyButtonClickedWhenInCall() {
+ mCallback.reset();
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
boolean result = super.onTouchEvent(ev);
// as long as the user is entering a pattern (i.e sending a touch event that was handled
@@ -133,11 +217,248 @@
}
@Override
- boolean disallowInterceptTouch(MotionEvent event) {
+ public void reset() {
+ // reset lock pattern
+ mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ mLockPatternView.enableInput();
+ mLockPatternView.setEnabled(true);
+ mLockPatternView.clearPattern();
+
+ if (mSecurityMessageDisplay == null) {
+ return;
+ }
+
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ } else {
+ displayDefaultSecurityMessage();
+ }
+ }
+
+ private void displayDefaultSecurityMessage() {
+ if (mSecurityMessageDisplay != null) {
+ mSecurityMessageDisplay.setMessage("");
+ }
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ @Override
+ public boolean disallowInterceptTouch(MotionEvent event) {
return !mLockPatternView.isEmpty()
|| mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY());
}
+ /** TODO: hook this up */
+ public void cleanUp() {
+ if (DEBUG) Log.v(TAG, "Cleanup() called on " + this);
+ mLockPatternUtils = null;
+ mLockPatternView.setOnPatternListener(null);
+ }
+
+ private class UnlockPatternListener implements LockPatternView.OnPatternListener {
+
+ @Override
+ public void onPatternStart() {
+ mLockPatternView.removeCallbacks(mCancelPatternRunnable);
+ mSecurityMessageDisplay.setMessage("");
+ }
+
+ @Override
+ public void onPatternCleared() {
+ }
+
+ @Override
+ public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
+ mCallback.userActivity();
+ mCallback.onUserInput();
+ }
+
+ @Override
+ public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
+ mKeyguardUpdateMonitor.setCredentialAttempted();
+ mLockPatternView.disableInput();
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ }
+
+ final int userId = KeyguardUpdateMonitor.getCurrentUser();
+ if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
+ mLockPatternView.enableInput();
+ onPatternChecked(userId, false, 0, false /* not valid - too short */);
+ return;
+ }
+
+ if (LatencyTracker.isEnabled(mContext)) {
+ LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
+ LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ LockscreenCredential.createPattern(pattern),
+ userId,
+ new LockPatternChecker.OnCheckCallback() {
+
+ @Override
+ public void onEarlyMatched() {
+ if (LatencyTracker.isEnabled(mContext)) {
+ LatencyTracker.getInstance(mContext).onActionEnd(
+ ACTION_CHECK_CREDENTIAL);
+ }
+ onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
+ true /* isValidPattern */);
+ }
+
+ @Override
+ public void onChecked(boolean matched, int timeoutMs) {
+ if (LatencyTracker.isEnabled(mContext)) {
+ LatencyTracker.getInstance(mContext).onActionEnd(
+ ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+ mLockPatternView.enableInput();
+ mPendingLockCheck = null;
+ if (!matched) {
+ onPatternChecked(userId, false /* matched */, timeoutMs,
+ true /* isValidPattern */);
+ }
+ }
+
+ @Override
+ public void onCancelled() {
+ // We already got dismissed with the early matched callback, so we
+ // cancelled the check. However, we still need to note down the latency.
+ if (LatencyTracker.isEnabled(mContext)) {
+ LatencyTracker.getInstance(mContext).onActionEnd(
+ ACTION_CHECK_CREDENTIAL_UNLOCKED);
+ }
+ }
+ });
+ if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
+ mCallback.userActivity();
+ mCallback.onUserInput();
+ }
+ }
+
+ private void onPatternChecked(int userId, boolean matched, int timeoutMs,
+ boolean isValidPattern) {
+ boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
+ if (matched) {
+ mCallback.reportUnlockAttempt(userId, true, 0);
+ if (dismissKeyguard) {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
+ mCallback.dismiss(true, userId);
+ }
+ } else {
+ mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
+ if (isValidPattern) {
+ mCallback.reportUnlockAttempt(userId, false, timeoutMs);
+ if (timeoutMs > 0) {
+ long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
+ userId, timeoutMs);
+ handleAttemptLockout(deadline);
+ }
+ }
+ if (timeoutMs == 0) {
+ mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern);
+ mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
+ }
+ }
+ }
+ }
+
+ private void handleAttemptLockout(long elapsedRealtimeDeadline) {
+ mLockPatternView.clearPattern();
+ mLockPatternView.setEnabled(false);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long secondsInFuture = (long) Math.ceil(
+ (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
+ mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
+ mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
+ R.plurals.kg_too_many_failed_attempts_countdown,
+ secondsRemaining, secondsRemaining));
+ }
+
+ @Override
+ public void onFinish() {
+ mLockPatternView.setEnabled(true);
+ displayDefaultSecurityMessage();
+ }
+
+ }.start();
+ }
+
+ @Override
+ public boolean needsInput() {
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ if (mCountdownTimer != null) {
+ mCountdownTimer.cancel();
+ mCountdownTimer = null;
+ }
+ if (mPendingLockCheck != null) {
+ mPendingLockCheck.cancel(false);
+ mPendingLockCheck = null;
+ }
+ displayDefaultSecurityMessage();
+ }
+
+ @Override
+ public void onResume(int reason) {
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ switch (reason) {
+ case PROMPT_REASON_RESTART:
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern);
+ break;
+ case PROMPT_REASON_TIMEOUT:
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ case PROMPT_REASON_DEVICE_ADMIN:
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_device_admin);
+ break;
+ case PROMPT_REASON_USER_REQUEST:
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request);
+ break;
+ case PROMPT_REASON_PREPARE_FOR_UPDATE:
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ case PROMPT_REASON_NONE:
+ break;
+ default:
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
+ }
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (colorState != null) {
+ mSecurityMessageDisplay.setNextMessageColor(colorState);
+ }
+ mSecurityMessageDisplay.setMessage(message);
+ }
+
+ @Override
public void startAppearAnimation() {
enableClipping(false);
setAlpha(1f);
@@ -146,7 +467,12 @@
0, mAppearAnimationUtils.getInterpolator());
mAppearAnimationUtils.startAnimation2d(
mLockPatternView.getCellStates(),
- () -> enableClipping(true),
+ new Runnable() {
+ @Override
+ public void run() {
+ enableClipping(true);
+ }
+ },
this);
if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
@@ -158,9 +484,11 @@
}
}
- public boolean startDisappearAnimation(boolean needsSlowUnlockTransition,
- final Runnable finishRunnable) {
- float durationMultiplier = needsSlowUnlockTransition ? DISAPPEAR_MULTIPLIER_LOCKED : 1f;
+ @Override
+ public boolean startDisappearAnimation(final Runnable finishRunnable) {
+ float durationMultiplier = mKeyguardUpdateMonitor.needsSlowUnlockTransition()
+ ? DISAPPEAR_MULTIPLIER_LOCKED
+ : 1f;
mLockPatternView.clearPattern();
enableClipping(false);
setTranslationY(0);
@@ -169,8 +497,10 @@
-mDisappearAnimationUtils.getStartTranslation(),
mDisappearAnimationUtils.getInterpolator());
- DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition
- ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils;
+ DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
+ .needsSlowUnlockTransition()
+ ? mDisappearAnimationUtilsLocked
+ : mDisappearAnimationUtils;
disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
() -> {
enableClipping(true);
@@ -219,7 +549,7 @@
@Override
public CharSequence getTitle() {
- return getResources().getString(
+ return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pattern_unlock);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
deleted file mode 100644
index 3db9db7..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
-
-import android.content.res.ColorStateList;
-import android.os.AsyncTask;
-import android.os.CountDownTimer;
-import android.os.SystemClock;
-import android.view.View;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternView;
-import com.android.internal.widget.LockPatternView.Cell;
-import com.android.internal.widget.LockscreenCredential;
-import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
-
-import java.util.List;
-
-public class KeyguardPatternViewController
- extends KeyguardInputViewController<KeyguardPatternView> {
-
- // how many cells the user has to cross before we poke the wakelock
- private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
-
- // how long before we clear the wrong pattern
- private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000;
-
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final LockPatternUtils mLockPatternUtils;
- private final LatencyTracker mLatencyTracker;
- private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
-
- private KeyguardMessageAreaController mMessageAreaController;
- private LockPatternView mLockPatternView;
- private CountDownTimer mCountdownTimer;
- private AsyncTask<?, ?, ?> mPendingLockCheck;
-
- private EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
- @Override
- public void onEmergencyButtonClickedWhenInCall() {
- getKeyguardSecurityCallback().reset();
- }
- };
-
- /**
- * Useful for clearing out the wrong pattern after a delay
- */
- private Runnable mCancelPatternRunnable = new Runnable() {
- @Override
- public void run() {
- mLockPatternView.clearPattern();
- }
- };
-
- private class UnlockPatternListener implements LockPatternView.OnPatternListener {
-
- @Override
- public void onPatternStart() {
- mLockPatternView.removeCallbacks(mCancelPatternRunnable);
- mMessageAreaController.setMessage("");
- }
-
- @Override
- public void onPatternCleared() {
- }
-
- @Override
- public void onPatternCellAdded(List<Cell> pattern) {
- getKeyguardSecurityCallback().userActivity();
- getKeyguardSecurityCallback().onUserInput();
- }
-
- @Override
- public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
- mKeyguardUpdateMonitor.setCredentialAttempted();
- mLockPatternView.disableInput();
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- }
-
- final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
- mLockPatternView.enableInput();
- onPatternChecked(userId, false, 0, false /* not valid - too short */);
- return;
- }
-
- mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
- mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- mPendingLockCheck = LockPatternChecker.checkCredential(
- mLockPatternUtils,
- LockscreenCredential.createPattern(pattern),
- userId,
- new LockPatternChecker.OnCheckCallback() {
-
- @Override
- public void onEarlyMatched() {
- mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);
- onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
- true /* isValidPattern */);
- }
-
- @Override
- public void onChecked(boolean matched, int timeoutMs) {
- mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- mLockPatternView.enableInput();
- mPendingLockCheck = null;
- if (!matched) {
- onPatternChecked(userId, false /* matched */, timeoutMs,
- true /* isValidPattern */);
- }
- }
-
- @Override
- public void onCancelled() {
- // We already got dismissed with the early matched callback, so we
- // cancelled the check. However, we still need to note down the latency.
- mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
- }
- });
- if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
- getKeyguardSecurityCallback().userActivity();
- getKeyguardSecurityCallback().onUserInput();
- }
- }
-
- private void onPatternChecked(int userId, boolean matched, int timeoutMs,
- boolean isValidPattern) {
- boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
- if (matched) {
- getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
- if (dismissKeyguard) {
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
- getKeyguardSecurityCallback().dismiss(true, userId);
- }
- } else {
- mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
- if (isValidPattern) {
- getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
- if (timeoutMs > 0) {
- long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
- userId, timeoutMs);
- handleAttemptLockout(deadline);
- }
- }
- if (timeoutMs == 0) {
- mMessageAreaController.setMessage(R.string.kg_wrong_pattern);
- mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
- }
- }
- }
- }
-
- protected KeyguardPatternViewController(KeyguardPatternView view,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- SecurityMode securityMode,
- LockPatternUtils lockPatternUtils,
- KeyguardSecurityCallback keyguardSecurityCallback,
- LatencyTracker latencyTracker,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory) {
- super(view, securityMode, keyguardSecurityCallback);
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mLockPatternUtils = lockPatternUtils;
- mLatencyTracker = latencyTracker;
- mMessageAreaControllerFactory = messageAreaControllerFactory;
- KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
- mMessageAreaController = mMessageAreaControllerFactory.create(kma);
- mLockPatternView = mView.findViewById(R.id.lockPatternView);
- }
-
- @Override
- public void init() {
- super.init();
- mMessageAreaController.init();
- }
-
- @Override
- protected void onViewAttached() {
- mLockPatternView.setOnPatternListener(new UnlockPatternListener());
- mLockPatternView.setSaveEnabled(false);
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
- // vibrate mode will be the same for the life of this screen
- mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled());
-
- EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(mEmergencyButtonCallback);
- }
-
- View cancelBtn = mView.findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- getKeyguardSecurityCallback().reset();
- getKeyguardSecurityCallback().onCancelClicked();
- });
- }
- }
-
- @Override
- protected void onViewDetached() {
- super.onViewDetached();
- mLockPatternView.setOnPatternListener(null);
- EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
- if (button != null) {
- button.setCallback(null);
- }
- View cancelBtn = mView.findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(null);
- }
- }
-
- @Override
- public void reset() {
- // reset lock pattern
- mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled(
- KeyguardUpdateMonitor.getCurrentUser()));
- mLockPatternView.enableInput();
- mLockPatternView.setEnabled(true);
- mLockPatternView.clearPattern();
-
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- } else {
- displayDefaultSecurityMessage();
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- if (mCountdownTimer != null) {
- mCountdownTimer.cancel();
- mCountdownTimer = null;
- }
-
- if (mPendingLockCheck != null) {
- mPendingLockCheck.cancel(false);
- mPendingLockCheck = null;
- }
- displayDefaultSecurityMessage();
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void showPromptReason(int reason) {
- /// TODO: move all this logic into the MessageAreaController?
- switch (reason) {
- case PROMPT_REASON_RESTART:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern);
- break;
- case PROMPT_REASON_TIMEOUT:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- case PROMPT_REASON_DEVICE_ADMIN:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin);
- break;
- case PROMPT_REASON_USER_REQUEST:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request);
- break;
- case PROMPT_REASON_PREPARE_FOR_UPDATE:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- case PROMPT_REASON_NONE:
- break;
- default:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
- }
- }
-
- @Override
- public void showMessage(CharSequence message, ColorStateList colorState) {
- if (colorState != null) {
- mMessageAreaController.setNextMessageColor(colorState);
- }
- mMessageAreaController.setMessage(message);
- }
-
- @Override
- public void startAppearAnimation() {
- super.startAppearAnimation();
- }
-
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return mView.startDisappearAnimation(
- mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
- }
-
- private void displayDefaultSecurityMessage() {
- mMessageAreaController.setMessage("");
- }
-
- private void handleAttemptLockout(long elapsedRealtimeDeadline) {
- mLockPatternView.clearPattern();
- mLockPatternView.setEnabled(false);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- final long secondsInFuture = (long) Math.ceil(
- (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
- mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {
-
- @Override
- public void onTick(long millisUntilFinished) {
- final int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
- mMessageAreaController.setMessage(mView.getResources().getQuantityString(
- R.plurals.kg_too_many_failed_attempts_countdown,
- secondsRemaining, secondsRemaining));
- }
-
- @Override
- public void onFinish() {
- mLockPatternView.setEnabled(true);
- displayDefaultSecurityMessage();
- }
-
- }.start();
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 7fa43116..c7f27cf 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -16,17 +16,11 @@
package com.android.keyguard;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
-
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import com.android.internal.widget.LockscreenCredential;
@@ -35,12 +29,22 @@
/**
* A Pin based Keyguard input view
*/
-public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView {
+public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
+ implements View.OnKeyListener, View.OnTouchListener {
protected PasswordTextView mPasswordEntry;
private View mOkButton;
private View mDeleteButton;
- private View[] mButtons = new View[10];
+ private View mButton0;
+ private View mButton1;
+ private View mButton2;
+ private View mButton3;
+ private View mButton4;
+ private View mButton5;
+ private View mButton6;
+ private View mButton7;
+ private View mButton8;
+ private View mButton9;
public KeyguardPinBasedInputView(Context context) {
this(context, null);
@@ -58,6 +62,7 @@
@Override
protected void resetState() {
+ setPasswordEntryEnabled(true);
}
@Override
@@ -81,10 +86,10 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.isConfirmKey(keyCode)) {
- mOkButton.performClick();
+ performClick(mOkButton);
return true;
} else if (keyCode == KeyEvent.KEYCODE_DEL) {
- mDeleteButton.performClick();
+ performClick(mDeleteButton);
return true;
}
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
@@ -120,9 +125,42 @@
}
}
+ private void performClick(View view) {
+ view.performClick();
+ }
+
private void performNumberClick(int number) {
- if (number >= 0 && number <= 9) {
- mButtons[number].performClick();
+ switch (number) {
+ case 0:
+ performClick(mButton0);
+ break;
+ case 1:
+ performClick(mButton1);
+ break;
+ case 2:
+ performClick(mButton2);
+ break;
+ case 3:
+ performClick(mButton3);
+ break;
+ case 4:
+ performClick(mButton4);
+ break;
+ case 5:
+ performClick(mButton5);
+ break;
+ case 6:
+ performClick(mButton6);
+ break;
+ case 7:
+ performClick(mButton7);
+ break;
+ case 8:
+ performClick(mButton8);
+ break;
+ case 9:
+ performClick(mButton9);
+ break;
}
}
@@ -139,31 +177,94 @@
@Override
protected void onFinishInflate() {
mPasswordEntry = findViewById(getPasswordTextViewId());
+ mPasswordEntry.setOnKeyListener(this);
// Set selected property on so the view can send accessibility events.
mPasswordEntry.setSelected(true);
+ mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() {
+ @Override
+ public void onUserActivity() {
+ onUserInput();
+ }
+ });
+
mOkButton = findViewById(R.id.key_enter);
+ if (mOkButton != null) {
+ mOkButton.setOnTouchListener(this);
+ mOkButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mPasswordEntry.isEnabled()) {
+ verifyPasswordAndUnlock();
+ }
+ }
+ });
+ mOkButton.setOnHoverListener(new LiftToActivateListener(getContext()));
+ }
mDeleteButton = findViewById(R.id.delete_button);
mDeleteButton.setVisibility(View.VISIBLE);
+ mDeleteButton.setOnTouchListener(this);
+ mDeleteButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ mPasswordEntry.deleteLastChar();
+ }
+ }
+ });
+ mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ // check for time-based lockouts
+ if (mPasswordEntry.isEnabled()) {
+ resetPasswordText(true /* animate */, true /* announce */);
+ }
+ doHapticKeyClick();
+ return true;
+ }
+ });
- mButtons[0] = findViewById(R.id.key0);
- mButtons[1] = findViewById(R.id.key1);
- mButtons[2] = findViewById(R.id.key2);
- mButtons[3] = findViewById(R.id.key3);
- mButtons[4] = findViewById(R.id.key4);
- mButtons[5] = findViewById(R.id.key5);
- mButtons[6] = findViewById(R.id.key6);
- mButtons[7] = findViewById(R.id.key7);
- mButtons[8] = findViewById(R.id.key8);
- mButtons[9] = findViewById(R.id.key9);
+ mButton0 = findViewById(R.id.key0);
+ mButton1 = findViewById(R.id.key1);
+ mButton2 = findViewById(R.id.key2);
+ mButton3 = findViewById(R.id.key3);
+ mButton4 = findViewById(R.id.key4);
+ mButton5 = findViewById(R.id.key5);
+ mButton6 = findViewById(R.id.key6);
+ mButton7 = findViewById(R.id.key7);
+ mButton8 = findViewById(R.id.key8);
+ mButton9 = findViewById(R.id.key9);
mPasswordEntry.requestFocus();
super.onFinishInflate();
}
@Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ mPasswordEntry.requestFocus();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ doHapticKeyClick();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ return onKeyDown(keyCode, event);
+ }
+ return false;
+ }
+
+ @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pin_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
deleted file mode 100644
index 4d0ebff..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnKeyListener;
-import android.view.View.OnTouchListener;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
-
-public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
- extends KeyguardAbsKeyInputViewController<T> {
-
- private final LiftToActivateListener mLiftToActivateListener;
- protected PasswordTextView mPasswordEntry;
-
- private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- return mView.onKeyDown(keyCode, event);
- }
- return false;
- };
-
- private final OnTouchListener mOnTouchListener = (v, event) -> {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mView.doHapticKeyClick();
- }
- return false;
- };
-
- protected KeyguardPinBasedInputViewController(T view,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- SecurityMode securityMode,
- LockPatternUtils lockPatternUtils,
- KeyguardSecurityCallback keyguardSecurityCallback,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- LatencyTracker latencyTracker,
- LiftToActivateListener liftToActivateListener) {
- super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
- messageAreaControllerFactory, latencyTracker);
- mLiftToActivateListener = liftToActivateListener;
- mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
- }
-
- @Override
- protected void onViewAttached() {
- super.onViewAttached();
-
- mPasswordEntry.setOnKeyListener(mOnKeyListener);
- mPasswordEntry.setUserActivityListener(this::onUserInput);
-
- View deleteButton = mView.findViewById(R.id.delete_button);
- deleteButton.setOnTouchListener(mOnTouchListener);
- deleteButton.setOnClickListener(v -> {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- mPasswordEntry.deleteLastChar();
- }
- });
- deleteButton.setOnLongClickListener(v -> {
- // check for time-based lockouts
- if (mPasswordEntry.isEnabled()) {
- mView.resetPasswordText(true /* animate */, true /* announce */);
- }
- mView.doHapticKeyClick();
- return true;
- });
-
- View okButton = mView.findViewById(R.id.key_enter);
- if (okButton != null) {
- okButton.setOnTouchListener(mOnTouchListener);
- okButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mPasswordEntry.isEnabled()) {
- verifyPasswordAndUnlock();
- }
- }
- });
- okButton.setOnHoverListener(mLiftToActivateListener);
- }
- }
-
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- mPasswordEntry.requestFocus();
- }
-
- @Override
- void resetState() {
- mView.setPasswordEntryEnabled(true);
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
deleted file mode 100644
index 6769436..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.view.View;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
-
-public class KeyguardPinViewController
- extends KeyguardPinBasedInputViewController<KeyguardPINView> {
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-
- protected KeyguardPinViewController(KeyguardPINView view,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- SecurityMode securityMode, LockPatternUtils lockPatternUtils,
- KeyguardSecurityCallback keyguardSecurityCallback,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- LatencyTracker latencyTracker,
- LiftToActivateListener liftToActivateListener) {
- super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
- messageAreaControllerFactory, latencyTracker, liftToActivateListener);
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- }
-
- @Override
- protected void onViewAttached() {
- super.onViewAttached();
-
- View cancelBtn = mView.findViewById(R.id.cancel_button);
- if (cancelBtn != null) {
- cancelBtn.setOnClickListener(view -> {
- getKeyguardSecurityCallback().reset();
- getKeyguardSecurityCallback().onCancelClicked();
- });
- }
- }
-
- @Override
- void resetState() {
- super.resetState();
- mMessageAreaController.setMessage("");
- }
-
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return mView.startDisappearAnimation(
- mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b62ea6b..81d37a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -19,6 +19,8 @@
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+import static com.android.systemui.DejankUtils.whitelistIpcs;
+
import static java.lang.Integer.max;
import android.animation.Animator;
@@ -26,14 +28,25 @@
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.Intent;
+import android.content.res.ColorStateList;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.metrics.LogMaker;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.MathUtils;
+import android.util.Slog;
import android.util.TypedValue;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
@@ -48,30 +61,42 @@
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.InjectionInflationController;
import java.util.List;
-public class KeyguardSecurityContainer extends FrameLayout {
- static final int USER_TYPE_PRIMARY = 1;
- static final int USER_TYPE_WORK_PROFILE = 2;
- static final int USER_TYPE_SECONDARY_USER = 3;
+public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
+ private static final boolean DEBUG = KeyguardConstants.DEBUG;
+ private static final String TAG = "KeyguardSecurityView";
+
+ private static final int USER_TYPE_PRIMARY = 1;
+ private static final int USER_TYPE_WORK_PROFILE = 2;
+ private static final int USER_TYPE_SECONDARY_USER = 3;
// Bouncer is dismissed due to no security.
- static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
+ private static final int BOUNCER_DISMISS_NONE_SECURITY = 0;
// Bouncer is dismissed due to pin, password or pattern entered.
- static final int BOUNCER_DISMISS_PASSWORD = 1;
+ private static final int BOUNCER_DISMISS_PASSWORD = 1;
// Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated.
- static final int BOUNCER_DISMISS_BIOMETRIC = 2;
+ private static final int BOUNCER_DISMISS_BIOMETRIC = 2;
// Bouncer is dismissed due to extended access granted.
- static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
+ private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3;
// Bouncer is dismissed due to sim card unlock code entered.
- static final int BOUNCER_DISMISS_SIM = 4;
+ private static final int BOUNCER_DISMISS_SIM = 4;
// Make the view move slower than the finger, as if the spring were applying force.
private static final float TOUCH_Y_MULTIPLIER = 0.25f;
@@ -80,23 +105,36 @@
// How much to scale the default slop by, to avoid accidental drags.
private static final float SLOP_SCALE = 4f;
+ private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
+
private static final long IME_DISAPPEAR_DURATION_MS = 125;
+ private KeyguardSecurityModel mSecurityModel;
+ private LockPatternUtils mLockPatternUtils;
+
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
+ private boolean mIsVerifyUnlockOnly;
+ private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
+ private KeyguardSecurityView mCurrentSecurityView;
+ private SecurityCallback mSecurityCallback;
private AlertDialog mAlertDialog;
+ private InjectionInflationController mInjectionInflationController;
private boolean mSwipeUpToRetry;
+ private AdminSecondaryLockScreenController mSecondaryLockScreenController;
private final ViewConfiguration mViewConfiguration;
private final SpringAnimation mSpringAnimation;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
+ private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardStateController mKeyguardStateController;
+ private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private float mLastTouchY = -1;
private int mActivePointerId = -1;
private boolean mIsDragging;
private float mStartTouchY = -1;
private boolean mDisappearAnimRunning;
- private SwipeListener mSwipeListener;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
@@ -148,22 +186,19 @@
// Used to notify the container when something interesting happens.
public interface SecurityCallback {
- boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen);
- void userActivity();
- void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
+ public boolean dismiss(boolean authenticated, int targetUserId,
+ boolean bypassSecondaryLockScreen);
+ public void userActivity();
+ public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
/**
* @param strongAuth wheher the user has authenticated with strong authentication like
* pattern, password or PIN but not by trust agents or fingerprint
* @param targetUserId a user that needs to be the foreground user at the finish completion.
*/
- void finish(boolean strongAuth, int targetUserId);
- void reset();
- void onCancelClicked();
- }
-
- public interface SwipeListener {
- void onSwipeUp();
+ public void finish(boolean strongAuth, int targetUserId);
+ public void reset();
+ public void onCancelClicked();
}
@VisibleForTesting
@@ -214,24 +249,52 @@
public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ mSecurityModel = Dependency.get(KeyguardSecurityModel.class);
+ mLockPatternUtils = new LockPatternUtils(context);
+ mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y);
+ mInjectionInflationController = new InjectionInflationController(
+ SystemUIFactory.getInstance().getSysUIComponent().createViewInstanceCreatorFactory());
mViewConfiguration = ViewConfiguration.get(context);
+ mKeyguardStateController = Dependency.get(KeyguardStateController.class);
+ mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this,
+ mUpdateMonitor, mCallback, new Handler(Looper.myLooper()));
}
- void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
+ public void setSecurityCallback(SecurityCallback callback) {
+ mSecurityCallback = callback;
+ }
+
+ @Override
+ public void onResume(int reason) {
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ getSecurityView(mCurrentSecuritySelection).onResume(reason);
+ }
mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
- updateBiometricRetry(securityMode, faceAuthEnabled);
+ updateBiometricRetry();
}
+ @Override
public void onPause() {
if (mAlertDialog != null) {
mAlertDialog.dismiss();
mAlertDialog = null;
}
+ mSecondaryLockScreenController.hide();
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ getSecurityView(mCurrentSecuritySelection).onPause();
+ }
mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
}
@Override
+ public void onStartingToHide() {
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ getSecurityView(mCurrentSecuritySelection).onStartingToHide();
+ }
+ }
+
+ @Override
public boolean shouldDelayChildPressedState() {
return true;
}
@@ -253,12 +316,13 @@
return false;
}
// Avoid dragging the pattern view
- if (mSecurityViewFlipper.getSecurityView().disallowInterceptTouch(event)) {
+ if (mCurrentSecurityView.disallowInterceptTouch(event)) {
return false;
}
int index = event.findPointerIndex(mActivePointerId);
float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE;
- if (index != -1 && mStartTouchY - event.getY(index) > touchSlop) {
+ if (mCurrentSecurityView != null && index != -1
+ && mStartTouchY - event.getY(index) > touchSlop) {
mIsDragging = true;
return true;
}
@@ -306,28 +370,31 @@
}
if (action == MotionEvent.ACTION_UP) {
if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- MIN_DRAG_SIZE, getResources().getDisplayMetrics())) {
- if (mSwipeListener != null) {
- mSwipeListener.onSwipeUp();
- }
+ MIN_DRAG_SIZE, getResources().getDisplayMetrics())
+ && !mUpdateMonitor.isFaceDetectionRunning()) {
+ mUpdateMonitor.requestFaceAuth();
+ mCallback.userActivity();
+ showMessage(null, null);
}
}
return true;
}
- void setSwipeListener(SwipeListener swipeListener) {
- mSwipeListener = swipeListener;
- }
-
private void startSpringAnimation(float startVelocity) {
mSpringAnimation
.setStartVelocity(startVelocity)
.animateToFinalPosition(0);
}
- public void startDisappearAnimation(SecurityMode securitySelection) {
+ public void startAppearAnimation() {
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
+ }
+ }
+
+ public boolean startDisappearAnimation(Runnable onFinishRunnable) {
mDisappearAnimRunning = true;
- if (securitySelection == SecurityMode.Password) {
+ if (mCurrentSecuritySelection == SecurityMode.Password) {
mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(),
IME_DISAPPEAR_DURATION_MS,
Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() {
@@ -372,13 +439,19 @@
}
});
}
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
+ onFinishRunnable);
+ }
+ return false;
}
/**
* Enables/disables swipe up to retry on the bouncer.
*/
- private void updateBiometricRetry(SecurityMode securityMode, boolean faceAuthEnabled) {
- mSwipeUpToRetry = faceAuthEnabled
+ private void updateBiometricRetry() {
+ SecurityMode securityMode = getSecurityMode();
+ mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled()
&& securityMode != SecurityMode.SimPin
&& securityMode != SecurityMode.SimPuk
&& securityMode != SecurityMode.None;
@@ -388,11 +461,53 @@
return mSecurityViewFlipper.getTitle();
}
+ @VisibleForTesting
+ protected KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
+ final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
+ KeyguardSecurityView view = null;
+ final int children = mSecurityViewFlipper.getChildCount();
+ for (int child = 0; child < children; child++) {
+ if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
+ view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
+ break;
+ }
+ }
+ int layoutId = getLayoutIdFor(securityMode);
+ if (view == null && layoutId != 0) {
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
+ if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
+ View v = mInjectionInflationController.injectable(inflater)
+ .inflate(layoutId, mSecurityViewFlipper, false);
+ mSecurityViewFlipper.addView(v);
+ updateSecurityView(v);
+ view = (KeyguardSecurityView)v;
+ view.reset();
+ }
+
+ return view;
+ }
+
+ private void updateSecurityView(View view) {
+ if (view instanceof KeyguardSecurityView) {
+ KeyguardSecurityView ksv = (KeyguardSecurityView) view;
+ ksv.setKeyguardCallback(mCallback);
+ ksv.setLockPatternUtils(mLockPatternUtils);
+ } else {
+ Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
+ }
+ }
@Override
public void onFinishInflate() {
super.onFinishInflate();
mSecurityViewFlipper = findViewById(R.id.view_flipper);
+ mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
+ }
+
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
+ mSecurityModel.setLockPatternUtils(utils);
+ mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
}
@Override
@@ -424,12 +539,11 @@
mAlertDialog.show();
}
- void showTimeoutDialog(int userId, int timeoutMs, LockPatternUtils lockPatternUtils,
- SecurityMode securityMode) {
- int timeoutInSeconds = timeoutMs / 1000;
+ private void showTimeoutDialog(int userId, int timeoutMs) {
+ int timeoutInSeconds = (int) timeoutMs / 1000;
int messageId = 0;
- switch (securityMode) {
+ switch (mSecurityModel.getSecurityMode(userId)) {
case Pattern:
messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
break;
@@ -449,13 +563,13 @@
if (messageId != 0) {
final String message = mContext.getString(messageId,
- lockPatternUtils.getCurrentFailedPasswordAttempts(userId),
+ mLockPatternUtils.getCurrentFailedPasswordAttempts(userId),
timeoutInSeconds);
showDialog(null, message);
}
}
- void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
+ private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
String message = null;
switch (userType) {
case USER_TYPE_PRIMARY:
@@ -474,7 +588,7 @@
showDialog(null, message);
}
- void showWipeDialog(int attempts, int userType) {
+ private void showWipeDialog(int attempts, int userType) {
String message = null;
switch (userType) {
case USER_TYPE_PRIMARY:
@@ -493,8 +607,358 @@
showDialog(null, message);
}
+ private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
+ // +1 for this time
+ final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
+
+ if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
+
+ final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
+ final int failedAttemptsBeforeWipe =
+ dpm.getMaximumFailedPasswordsForWipe(null, userId);
+
+ final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
+ (failedAttemptsBeforeWipe - failedAttempts)
+ : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
+ if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
+ // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
+ // N attempts. Once we get below the grace period, we post this dialog every time as a
+ // clear warning until the deletion fires.
+ // Check which profile has the strictest policy for failed password attempts
+ final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
+ int userType = USER_TYPE_PRIMARY;
+ if (expiringUser == userId) {
+ // TODO: http://b/23522538
+ if (expiringUser != UserHandle.USER_SYSTEM) {
+ userType = USER_TYPE_SECONDARY_USER;
+ }
+ } else if (expiringUser != UserHandle.USER_NULL) {
+ userType = USER_TYPE_WORK_PROFILE;
+ } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
+ if (remainingBeforeWipe > 0) {
+ showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
+ } else {
+ // Too many attempts. The device will be wiped shortly.
+ Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
+ showWipeDialog(failedAttempts, userType);
+ }
+ }
+ mLockPatternUtils.reportFailedPasswordAttempt(userId);
+ if (timeoutMs > 0) {
+ mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
+ showTimeoutDialog(userId, timeoutMs);
+ }
+ }
+
+ /**
+ * Shows the primary security screen for the user. This will be either the multi-selector
+ * or the user's security method.
+ * @param turningOff true if the device is being turned off
+ */
+ void showPrimarySecurityScreen(boolean turningOff) {
+ SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser()));
+ if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
+ showSecurityScreen(securityMode);
+ }
+
+ /**
+ * Shows the next security screen if there is one.
+ * @param authenticated true if the user entered the correct authentication
+ * @param targetUserId a user that needs to be the foreground user at the finish (if called)
+ * completion.
+ * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
+ * secondary lock screen requirement, if any.
+ * @return true if keyguard is done
+ */
+ boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
+ boolean bypassSecondaryLockScreen) {
+ if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
+ boolean finish = false;
+ boolean strongAuth = false;
+ int eventSubtype = -1;
+ BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
+ if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
+ } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC;
+ } else if (SecurityMode.None == mCurrentSecuritySelection) {
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (SecurityMode.None == securityMode) {
+ finish = true; // no security required
+ eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
+ } else {
+ showSecurityScreen(securityMode); // switch to the alternate security view
+ }
+ } else if (authenticated) {
+ switch (mCurrentSecuritySelection) {
+ case Pattern:
+ case Password:
+ case PIN:
+ strongAuth = true;
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_PASSWORD;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
+ break;
+
+ case SimPin:
+ case SimPuk:
+ // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
+ SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
+ if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
+ KeyguardUpdateMonitor.getCurrentUser())) {
+ finish = true;
+ eventSubtype = BOUNCER_DISMISS_SIM;
+ uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
+ } else {
+ showSecurityScreen(securityMode);
+ }
+ break;
+
+ default:
+ Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
+ showPrimarySecurityScreen(false);
+ break;
+ }
+ }
+ // Check for device admin specified additional security measures.
+ if (finish && !bypassSecondaryLockScreen) {
+ Intent secondaryLockscreenIntent =
+ mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
+ if (secondaryLockscreenIntent != null) {
+ mSecondaryLockScreenController.show(secondaryLockscreenIntent);
+ return false;
+ }
+ }
+ if (eventSubtype != -1) {
+ mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
+ .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
+ }
+ if (uiEvent != BouncerUiEvent.UNKNOWN) {
+ sUiEventLogger.log(uiEvent);
+ }
+ if (finish) {
+ mSecurityCallback.finish(strongAuth, targetUserId);
+ }
+ return finish;
+ }
+
+ /**
+ * Switches to the given security view unless it's already being shown, in which case
+ * this is a no-op.
+ *
+ * @param securityMode
+ */
+ private void showSecurityScreen(SecurityMode securityMode) {
+ if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
+
+ if (securityMode == mCurrentSecuritySelection) return;
+
+ KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
+ KeyguardSecurityView newView = getSecurityView(securityMode);
+
+ // Emulate Activity life cycle
+ if (oldView != null) {
+ oldView.onPause();
+ oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
+ }
+ if (securityMode != SecurityMode.None) {
+ newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
+ newView.setKeyguardCallback(mCallback);
+ }
+
+ // Find and show this child.
+ final int childCount = mSecurityViewFlipper.getChildCount();
+
+ final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
+ for (int i = 0; i < childCount; i++) {
+ if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
+ mSecurityViewFlipper.setDisplayedChild(i);
+ break;
+ }
+ }
+
+ mCurrentSecuritySelection = securityMode;
+ mCurrentSecurityView = newView;
+ mSecurityCallback.onSecurityModeChanged(securityMode,
+ securityMode != SecurityMode.None && newView.needsInput());
+ }
+
+ private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
+ public void userActivity() {
+ if (mSecurityCallback != null) {
+ mSecurityCallback.userActivity();
+ }
+ }
+
+ @Override
+ public void onUserInput() {
+ mUpdateMonitor.cancelFaceAuth();
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId) {
+ dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
+ }
+
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) {
+ mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
+ }
+
+ public boolean isVerifyUnlockOnly() {
+ return mIsVerifyUnlockOnly;
+ }
+
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
+ if (success) {
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
+ mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
+ // Force a garbage collection in an attempt to erase any lockscreen password left in
+ // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
+ // dismiss animation janky.
+ ThreadUtils.postOnBackgroundThread(() -> {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException ignored) { }
+ Runtime.getRuntime().gc();
+ });
+ } else {
+ SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
+ KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
+ }
+ mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
+ .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
+ sUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
+ : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
+ }
+
+ public void reset() {
+ mSecurityCallback.reset();
+ }
+
+ public void onCancelClicked() {
+ mSecurityCallback.onCancelClicked();
+ }
+ };
+
+ // The following is used to ignore callbacks from SecurityViews that are no longer current
+ // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
+ // state for the current security method.
+ private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
+ @Override
+ public void userActivity() { }
+ @Override
+ public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
+ @Override
+ public boolean isVerifyUnlockOnly() { return false; }
+ @Override
+ public void dismiss(boolean securityVerified, int targetUserId) { }
+ @Override
+ public void dismiss(boolean authenticated, int targetId,
+ boolean bypassSecondaryLockScreen) { }
+ @Override
+ public void onUserInput() { }
+ @Override
+ public void reset() {}
+ };
+
+ private int getSecurityViewIdForMode(SecurityMode securityMode) {
+ switch (securityMode) {
+ case Pattern: return R.id.keyguard_pattern_view;
+ case PIN: return R.id.keyguard_pin_view;
+ case Password: return R.id.keyguard_password_view;
+ case SimPin: return R.id.keyguard_sim_pin_view;
+ case SimPuk: return R.id.keyguard_sim_puk_view;
+ }
+ return 0;
+ }
+
+ @VisibleForTesting
+ public int getLayoutIdFor(SecurityMode securityMode) {
+ switch (securityMode) {
+ case Pattern: return R.layout.keyguard_pattern_view;
+ case PIN: return R.layout.keyguard_pin_view;
+ case Password: return R.layout.keyguard_password_view;
+ case SimPin: return R.layout.keyguard_sim_pin_view;
+ case SimPuk: return R.layout.keyguard_sim_puk_view;
+ default:
+ return 0;
+ }
+ }
+
+ public SecurityMode getSecurityMode() {
+ return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
+ }
+
+ public SecurityMode getCurrentSecurityMode() {
+ return mCurrentSecuritySelection;
+ }
+
+ public KeyguardSecurityView getCurrentSecurityView() {
+ return mCurrentSecurityView;
+ }
+
+ public void verifyUnlock() {
+ mIsVerifyUnlockOnly = true;
+ showSecurityScreen(getSecurityMode());
+ }
+
+ public SecurityMode getCurrentSecuritySelection() {
+ return mCurrentSecuritySelection;
+ }
+
+ public void dismiss(boolean authenticated, int targetUserId) {
+ mCallback.dismiss(authenticated, targetUserId);
+ }
+
+ public boolean needsInput() {
+ return mSecurityViewFlipper.needsInput();
+ }
+
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ mSecurityViewFlipper.setKeyguardCallback(callback);
+ }
+
+ @Override
public void reset() {
+ mSecurityViewFlipper.reset();
mDisappearAnimRunning = false;
}
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ return mSecurityViewFlipper.getCallback();
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ if (reason != PROMPT_REASON_NONE) {
+ Log.i(TAG, "Strong auth required, reason: " + reason);
+ }
+ getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
+ }
+ }
+
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState);
+ }
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ mSecurityViewFlipper.showUsabilityHint();
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 64676e5..17f25bd08ef 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -16,166 +16,33 @@
package com.android.keyguard;
-import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC;
-import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS;
-import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY;
-import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_PASSWORD;
-import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM;
-import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY;
-import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER;
-import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.Intent;
import android.content.res.ColorStateList;
-import android.metrics.LogMaker;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.Slog;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent;
import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
-import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.keyguard.dagger.KeyguardBouncerScope;
-import com.android.settingslib.utils.ThreadUtils;
-import com.android.systemui.shared.system.SysUiStatsLog;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
/** Controller for {@link KeyguardSecurityContainer} */
-@KeyguardBouncerScope
-public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer>
- implements KeyguardSecurityView {
+public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> {
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
- private static final String TAG = "KeyguardSecurityView";
-
- private final AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
private final LockPatternUtils mLockPatternUtils;
- private final KeyguardUpdateMonitor mUpdateMonitor;
- private final KeyguardSecurityModel mSecurityModel;
- private final MetricsLogger mMetricsLogger;
- private final UiEventLogger mUiEventLogger;
- private final KeyguardStateController mKeyguardStateController;
- private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController;
-
- private SecurityCallback mSecurityCallback;
- private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid;
-
- private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() {
- public void userActivity() {
- if (mSecurityCallback != null) {
- mSecurityCallback.userActivity();
- }
- }
-
- @Override
- public void onUserInput() {
- mUpdateMonitor.cancelFaceAuth();
- }
-
- @Override
- public void dismiss(boolean authenticated, int targetId) {
- dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false);
- }
-
- @Override
- public void dismiss(boolean authenticated, int targetId,
- boolean bypassSecondaryLockScreen) {
- mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen);
- }
-
- public boolean isVerifyUnlockOnly() {
- return false;
- }
-
- public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
- if (success) {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
- mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
- // Force a garbage collection in an attempt to erase any lockscreen password left in
- // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard
- // dismiss animation janky.
- ThreadUtils.postOnBackgroundThread(() -> {
- try {
- Thread.sleep(5000);
- } catch (InterruptedException ignored) { }
- Runtime.getRuntime().gc();
- });
- } else {
- SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
- SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
- reportFailedUnlockAttempt(userId, timeoutMs);
- }
- mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
- .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE));
- mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS
- : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE);
- }
-
- public void reset() {
- mSecurityCallback.reset();
- }
-
- public void onCancelClicked() {
- mSecurityCallback.onCancelClicked();
- }
- };
-
-
- private SwipeListener mSwipeListener = new SwipeListener() {
- @Override
- public void onSwipeUp() {
- if (!mUpdateMonitor.isFaceDetectionRunning()) {
- mUpdateMonitor.requestFaceAuth();
- mKeyguardSecurityCallback.userActivity();
- showMessage(null, null);
- }
- }
- };
+ private final KeyguardSecurityViewController.Factory mKeyguardSecurityViewControllerFactory;
@Inject
KeyguardSecurityContainerController(KeyguardSecurityContainer view,
- AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
LockPatternUtils lockPatternUtils,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardSecurityModel keyguardSecurityModel,
- MetricsLogger metricsLogger,
- UiEventLogger uiEventLogger,
- KeyguardStateController keyguardStateController,
- KeyguardSecurityViewFlipperController securityViewFlipperController) {
+ KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) {
super(view);
mLockPatternUtils = lockPatternUtils;
- mUpdateMonitor = keyguardUpdateMonitor;
- mSecurityModel = keyguardSecurityModel;
- mMetricsLogger = metricsLogger;
- mUiEventLogger = uiEventLogger;
- mKeyguardStateController = keyguardStateController;
- mSecurityViewFlipperController = securityViewFlipperController;
- mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create(
- mKeyguardSecurityCallback);
- }
-
- @Override
- public void init() {
- super.init();
- mSecurityViewFlipperController.init();
+ view.setLockPatternUtils(mLockPatternUtils);
+ mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
}
@Override
protected void onViewAttached() {
- mView.setSwipeListener(mSwipeListener);
}
@Override
@@ -184,270 +51,68 @@
/** */
public void onPause() {
- mAdminSecondaryLockScreenController.hide();
- if (mCurrentSecurityMode != SecurityMode.None) {
- getCurrentSecurityController().onPause();
- }
mView.onPause();
}
-
- /**
- * Shows the primary security screen for the user. This will be either the multi-selector
- * or the user's security method.
- * @param turningOff true if the device is being turned off
- */
public void showPrimarySecurityScreen(boolean turningOff) {
- SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode(
- KeyguardUpdateMonitor.getCurrentUser()));
- if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
- showSecurityScreen(securityMode);
+ mView.showPrimarySecurityScreen(turningOff);
}
- @Override
public void showPromptReason(int reason) {
- if (mCurrentSecurityMode != SecurityMode.None) {
- if (reason != PROMPT_REASON_NONE) {
- Log.i(TAG, "Strong auth required, reason: " + reason);
- }
- getCurrentSecurityController().showPromptReason(reason);
- }
+ mView.showPromptReason(reason);
}
public void showMessage(CharSequence message, ColorStateList colorState) {
- if (mCurrentSecurityMode != SecurityMode.None) {
- getCurrentSecurityController().showMessage(message, colorState);
- }
+ mView.showMessage(message, colorState);
}
- public SecurityMode getCurrentSecurityMode() {
- return mCurrentSecurityMode;
+ public SecurityMode getCurrentSecuritySelection() {
+ return mView.getCurrentSecuritySelection();
}
public void dismiss(boolean authenticated, int targetUserId) {
- mKeyguardSecurityCallback.dismiss(authenticated, targetUserId);
+ mView.dismiss(authenticated, targetUserId);
}
public void reset() {
mView.reset();
- mSecurityViewFlipperController.reset();
}
public CharSequence getTitle() {
return mView.getTitle();
}
- @Override
- public void onResume(int reason) {
- if (mCurrentSecurityMode != SecurityMode.None) {
- getCurrentSecurityController().onResume(reason);
- }
- mView.onResume(
- mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()),
- mKeyguardStateController.isFaceAuthEnabled());
+ public void onResume(int screenOn) {
+ mView.onResume(screenOn);
}
public void startAppearAnimation() {
- if (mCurrentSecurityMode != SecurityMode.None) {
- getCurrentSecurityController().startAppearAnimation();
- }
+ mView.startAppearAnimation();
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
- mView.startDisappearAnimation(getCurrentSecurityMode());
-
- if (mCurrentSecurityMode != SecurityMode.None) {
- return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable);
- }
-
- return false;
+ return mView.startDisappearAnimation(onFinishRunnable);
}
public void onStartingToHide() {
- if (mCurrentSecurityMode != SecurityMode.None) {
- getCurrentSecurityController().onStartingToHide();
- }
+ mView.onStartingToHide();
}
public void setSecurityCallback(SecurityCallback securityCallback) {
- mSecurityCallback = securityCallback;
+ mView.setSecurityCallback(securityCallback);
}
- /**
- * Shows the next security screen if there is one.
- * @param authenticated true if the user entered the correct authentication
- * @param targetUserId a user that needs to be the foreground user at the finish (if called)
- * completion.
- * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary
- * secondary lock screen requirement, if any.
- * @return true if keyguard is done
- */
public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
-
- if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
- boolean finish = false;
- boolean strongAuth = false;
- int eventSubtype = -1;
- BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
- if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS;
- } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_BIOMETRIC;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC;
- } else if (SecurityMode.None == getCurrentSecurityMode()) {
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (SecurityMode.None == securityMode) {
- finish = true; // no security required
- eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
- } else {
- showSecurityScreen(securityMode); // switch to the alternate security view
- }
- } else if (authenticated) {
- switch (getCurrentSecurityMode()) {
- case Pattern:
- case Password:
- case PIN:
- strongAuth = true;
- finish = true;
- eventSubtype = BOUNCER_DISMISS_PASSWORD;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
- break;
-
- case SimPin:
- case SimPuk:
- // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
- SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())) {
- finish = true;
- eventSubtype = BOUNCER_DISMISS_SIM;
- uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
- } else {
- showSecurityScreen(securityMode);
- }
- break;
-
- default:
- Log.v(TAG, "Bad security screen " + getCurrentSecurityMode()
- + ", fail safe");
- showPrimarySecurityScreen(false);
- break;
- }
- }
- // Check for device admin specified additional security measures.
- if (finish && !bypassSecondaryLockScreen) {
- Intent secondaryLockscreenIntent =
- mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
- if (secondaryLockscreenIntent != null) {
- mAdminSecondaryLockScreenController.show(secondaryLockscreenIntent);
- return false;
- }
- }
- if (eventSubtype != -1) {
- mMetricsLogger.write(new LogMaker(MetricsProto.MetricsEvent.BOUNCER)
- .setType(MetricsProto.MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
- }
- if (uiEvent != BouncerUiEvent.UNKNOWN) {
- mUiEventLogger.log(uiEvent);
- }
- if (finish) {
- mSecurityCallback.finish(strongAuth, targetUserId);
- }
- return finish;
+ return mView.showNextSecurityScreenOrFinish(
+ authenticated, targetUserId, bypassSecondaryLockScreen);
}
public boolean needsInput() {
- return getCurrentSecurityController().needsInput();
+ return mView.needsInput();
}
- /**
- * Switches to the given security view unless it's already being shown, in which case
- * this is a no-op.
- *
- * @param securityMode
- */
- @VisibleForTesting
- void showSecurityScreen(SecurityMode securityMode) {
- if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
-
- if (securityMode == SecurityMode.Invalid || securityMode == mCurrentSecurityMode) {
- return;
- }
-
- KeyguardInputViewController<KeyguardInputView> oldView = getCurrentSecurityController();
-
- // Emulate Activity life cycle
- if (oldView != null) {
- oldView.onPause();
- }
-
- KeyguardInputViewController<KeyguardInputView> newView = changeSecurityMode(securityMode);
- if (newView != null) {
- newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
- mSecurityViewFlipperController.show(newView);
- }
-
- mSecurityCallback.onSecurityModeChanged(
- securityMode, newView != null && newView.needsInput());
- }
-
- public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
- // +1 for this time
- final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1;
-
- if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
-
- final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
- final int failedAttemptsBeforeWipe =
- dpm.getMaximumFailedPasswordsForWipe(null, userId);
-
- final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0
- ? (failedAttemptsBeforeWipe - failedAttempts)
- : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
- if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
- // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
- // N attempts. Once we get below the grace period, we post this dialog every time as a
- // clear warning until the deletion fires.
- // Check which profile has the strictest policy for failed password attempts
- final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
- int userType = USER_TYPE_PRIMARY;
- if (expiringUser == userId) {
- // TODO: http://b/23522538
- if (expiringUser != UserHandle.USER_SYSTEM) {
- userType = USER_TYPE_SECONDARY_USER;
- }
- } else if (expiringUser != UserHandle.USER_NULL) {
- userType = USER_TYPE_WORK_PROFILE;
- } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
- if (remainingBeforeWipe > 0) {
- mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
- } else {
- // Too many attempts. The device will be wiped shortly.
- Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
- mView.showWipeDialog(failedAttempts, userType);
- }
- }
- mLockPatternUtils.reportFailedPasswordAttempt(userId);
- if (timeoutMs > 0) {
- mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
- mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils,
- mSecurityModel.getSecurityMode(userId));
- }
- }
-
- private KeyguardInputViewController<KeyguardInputView> getCurrentSecurityController() {
- return mSecurityViewFlipperController
- .getSecurityView(mCurrentSecurityMode, mKeyguardSecurityCallback);
- }
-
- private KeyguardInputViewController<KeyguardInputView> changeSecurityMode(
- SecurityMode securityMode) {
- mCurrentSecurityMode = securityMode;
- return getCurrentSecurityController();
+ public SecurityMode getCurrentSecurityMode() {
+ return mView.getCurrentSecurityMode();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
index c77c867..ac2160e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java
@@ -18,14 +18,13 @@
import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.app.admin.DevicePolicyManager;
-import android.content.res.Resources;
+import android.content.Context;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.Dependency;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
@@ -34,7 +33,7 @@
/**
* The different types of security available.
- * @see KeyguardSecurityContainerController#showSecurityScreen
+ * @see KeyguardSecurityContainer#showSecurityScreen
*/
public enum SecurityMode {
Invalid, // NULL state
@@ -46,15 +45,21 @@
SimPuk // Unlock by entering a sim puk
}
+ private final Context mContext;
private final boolean mIsPukScreenAvailable;
- private final LockPatternUtils mLockPatternUtils;
+ private LockPatternUtils mLockPatternUtils;
@Inject
- KeyguardSecurityModel(@Main Resources resources, LockPatternUtils lockPatternUtils) {
- mIsPukScreenAvailable = resources.getBoolean(
+ KeyguardSecurityModel(Context context) {
+ mContext = context;
+ mLockPatternUtils = new LockPatternUtils(context);
+ mIsPukScreenAvailable = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enable_puk_unlock_screen);
- mLockPatternUtils = lockPatternUtils;
+ }
+
+ void setLockPatternUtils(LockPatternUtils utils) {
+ mLockPatternUtils = utils;
}
public SecurityMode getSecurityMode(int userId) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index ac00e94..43cef3a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -18,9 +18,11 @@
import android.content.res.ColorStateList;
import android.view.MotionEvent;
+import com.android.internal.widget.LockPatternUtils;
+
public interface KeyguardSecurityView {
- int SCREEN_ON = 1;
- int VIEW_REVEALED = 2;
+ static public final int SCREEN_ON = 1;
+ static public final int VIEW_REVEALED = 2;
int PROMPT_REASON_NONE = 0;
@@ -61,6 +63,18 @@
int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
/**
+ * Interface back to keyguard to tell it when security
+ * @param callback
+ */
+ void setKeyguardCallback(KeyguardSecurityCallback callback);
+
+ /**
+ * Set {@link LockPatternUtils} object. Useful for providing a mock interface.
+ * @param utils
+ */
+ void setLockPatternUtils(LockPatternUtils utils);
+
+ /**
* Reset the view and prepare to take input. This should do things like clearing the
* password or pattern and clear error messages.
*/
@@ -87,6 +101,12 @@
boolean needsInput();
/**
+ * Get {@link KeyguardSecurityCallback} for the given object
+ * @return KeyguardSecurityCallback
+ */
+ KeyguardSecurityCallback getCallback();
+
+ /**
* Show a string explaining why the security view needs to be solved.
*
* @param reason a flag indicating which string should be shown, see {@link #PROMPT_REASON_NONE}
@@ -103,6 +123,12 @@
void showMessage(CharSequence message, ColorStateList colorState);
/**
+ * Instruct the view to show usability hints, if any.
+ *
+ */
+ void showUsabilityHint();
+
+ /**
* Starts the animation which should run when the security view appears.
*/
void startAppearAnimation();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
new file mode 100644
index 0000000..ef9ba19
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.keyguard;
+
+import android.view.View;
+
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+
+
+/** Controller for a {@link KeyguardSecurityView}. */
+public class KeyguardSecurityViewController extends ViewController<View> {
+
+ private final KeyguardSecurityView mView;
+
+ private KeyguardSecurityViewController(KeyguardSecurityView view) {
+ super((View) view);
+ // KeyguardSecurityView isn't actually a View, so we need to track it ourselves.
+ mView = view;
+ }
+
+ @Override
+ protected void onViewAttached() {
+
+ }
+
+ @Override
+ protected void onViewDetached() {
+
+ }
+
+ /** Factory for a {@link KeyguardSecurityViewController}. */
+ public static class Factory {
+ @Inject
+ public Factory() {
+ }
+
+ /** Create a new {@link KeyguardSecurityViewController}. */
+ public KeyguardSecurityViewController create(KeyguardSecurityView view) {
+ return new KeyguardSecurityViewController(view);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index b8439af..24da3ad 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -30,6 +31,7 @@
import android.widget.FrameLayout;
import android.widget.ViewFlipper;
+import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
/**
@@ -37,7 +39,7 @@
* we can emulate {@link android.view.WindowManager.LayoutParams#FLAG_SLIPPERY} within a view
* hierarchy.
*/
-public class KeyguardSecurityViewFlipper extends ViewFlipper {
+public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView {
private static final String TAG = "KeyguardSecurityViewFlipper";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -67,16 +69,111 @@
return result;
}
- KeyguardInputView getSecurityView() {
+ KeyguardSecurityView getSecurityView() {
View child = getChildAt(getDisplayedChild());
- if (child instanceof KeyguardInputView) {
- return (KeyguardInputView) child;
+ if (child instanceof KeyguardSecurityView) {
+ return (KeyguardSecurityView) child;
}
return null;
}
+ @Override
+ public void setKeyguardCallback(KeyguardSecurityCallback callback) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.setKeyguardCallback(callback);
+ }
+ }
+
+ @Override
+ public void setLockPatternUtils(LockPatternUtils utils) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.setLockPatternUtils(utils);
+ }
+ }
+
+ @Override
+ public void reset() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.reset();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.onPause();
+ }
+ }
+
+ @Override
+ public void onResume(int reason) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.onResume(reason);
+ }
+ }
+
+ @Override
+ public boolean needsInput() {
+ KeyguardSecurityView ksv = getSecurityView();
+ return (ksv != null) ? ksv.needsInput() : false;
+ }
+
+ @Override
+ public KeyguardSecurityCallback getCallback() {
+ KeyguardSecurityView ksv = getSecurityView();
+ return (ksv != null) ? ksv.getCallback() : null;
+ }
+
+ @Override
+ public void showPromptReason(int reason) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.showPromptReason(reason);
+ }
+ }
+
+ @Override
+ public void showMessage(CharSequence message, ColorStateList colorState) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.showMessage(message, colorState);
+ }
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.showUsabilityHint();
+ }
+ }
+
+ @Override
+ public void startAppearAnimation() {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.startAppearAnimation();
+ }
+ }
+
+ @Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ return ksv.startDisappearAnimation(finishRunnable);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
public CharSequence getTitle() {
- KeyguardInputView ksv = getSecurityView();
+ KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
return ksv.getTitle();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
deleted file mode 100644
index 4953035..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.util.Log;
-import android.view.LayoutInflater;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.KeyguardInputViewController.Factory;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.keyguard.dagger.KeyguardBouncerScope;
-import com.android.systemui.R;
-import com.android.systemui.util.ViewController;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Controller for a {@link KeyguardSecurityViewFlipper}.
- */
-@KeyguardBouncerScope
-public class KeyguardSecurityViewFlipperController
- extends ViewController<KeyguardSecurityViewFlipper> {
-
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
- private static final String TAG = "KeyguardSecurityView";
-
- private final List<KeyguardInputViewController<KeyguardInputView>> mChildren =
- new ArrayList<>();
- private final LayoutInflater mLayoutInflater;
- private final Factory mKeyguardSecurityViewControllerFactory;
-
- @Inject
- protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view,
- LayoutInflater layoutInflater,
- KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory) {
- super(view);
- mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
- mLayoutInflater = layoutInflater;
- }
-
- @Override
- protected void onViewAttached() {
-
- }
-
- @Override
- protected void onViewDetached() {
-
- }
-
- public void reset() {
- for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
- child.reset();
- }
- }
-
- @VisibleForTesting
- KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode,
- KeyguardSecurityCallback keyguardSecurityCallback) {
- KeyguardInputViewController<KeyguardInputView> childController = null;
- for (KeyguardInputViewController<KeyguardInputView> child : mChildren) {
- if (child.getSecurityMode() == securityMode) {
- childController = child;
- break;
- }
- }
-
- if (childController == null
- && securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) {
-
- int layoutId = getLayoutIdFor(securityMode);
- KeyguardInputView view = null;
- if (layoutId != 0) {
- if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
- view = (KeyguardInputView) mLayoutInflater.inflate(
- layoutId, mView, false);
- mView.addView(view);
- childController = mKeyguardSecurityViewControllerFactory.create(
- view, securityMode, keyguardSecurityCallback);
- childController.init();
-
- mChildren.add(childController);
- }
- }
-
- if (childController == null) {
- childController = new NullKeyguardInputViewController(
- securityMode, keyguardSecurityCallback);
- }
-
- return childController;
- }
-
- private int getLayoutIdFor(SecurityMode securityMode) {
- switch (securityMode) {
- case Pattern: return com.android.systemui.R.layout.keyguard_pattern_view;
- case PIN: return com.android.systemui.R.layout.keyguard_pin_view;
- case Password: return com.android.systemui.R.layout.keyguard_password_view;
- case SimPin: return com.android.systemui.R.layout.keyguard_sim_pin_view;
- case SimPuk: return R.layout.keyguard_sim_puk_view;
- default:
- return 0;
- }
- }
-
- /** Makes the supplied child visible if it is contained win this view, */
- public void show(KeyguardInputViewController<KeyguardInputView> childController) {
- int index = childController.getIndexIn(mView);
- if (index != -1) {
- mView.setDisplayedChild(index);
- }
- }
-
- private static class NullKeyguardInputViewController
- extends KeyguardInputViewController<KeyguardInputView> {
- protected NullKeyguardInputViewController(SecurityMode securityMode,
- KeyguardSecurityCallback keyguardSecurityCallback) {
- super(null, securityMode, keyguardSecurityCallback);
- }
-
- @Override
- public boolean needsInput() {
- return false;
- }
-
- @Override
- public void onStartingToHide() {
-
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index c0f9ce7..1c47aa0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -16,19 +16,66 @@
package com.android.keyguard;
+import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.app.ProgressDialog;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.telephony.PinResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
/**
* Displays a PIN pad for unlocking.
*/
public class KeyguardSimPinView extends KeyguardPinBasedInputView {
+ private static final String LOG_TAG = "KeyguardSimPinView";
+ private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
public static final String TAG = "KeyguardSimPinView";
+ private ProgressDialog mSimUnlockProgressDialog = null;
+ private CheckSimPin mCheckSimPinThread;
+
+ // Below flag is set to true during power-up or when a new SIM card inserted on device.
+ // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
+ // be displayed to inform user about the number of remaining PIN attempts left.
+ private boolean mShowDefaultMessage = true;
+ private int mRemainingAttempts = -1;
+ private AlertDialog mRemainingAttemptsDialog;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private ImageView mSimImageView;
+
+ KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ switch(simState) {
+ case TelephonyManager.SIM_STATE_READY: {
+ mRemainingAttempts = -1;
+ resetState();
+ break;
+ }
+ default:
+ resetState();
+ }
+ }
+ };
+
public KeyguardSimPinView(Context context) {
this(context, null);
}
@@ -37,9 +84,81 @@
super(context, attrs);
}
- public void setEsimLocked(boolean locked) {
+ @Override
+ public void resetState() {
+ super.resetState();
+ if (DEBUG) Log.v(TAG, "Resetting state");
+ handleSubInfoChangeIfNeeded();
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
+
KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
+ esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
+ }
+
+ private void setLockedSimMessage() {
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
+ int count = 1;
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ if (telephonyManager != null) {
+ count = telephonyManager.getActiveModemCount();
+ }
+ Resources rez = getResources();
+ String msg;
+ TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
+ int color = array.getColor(0, Color.WHITE);
+ array.recycle();
+ if (count < 2) {
+ msg = rez.getString(R.string.kg_sim_pin_instructions);
+ } else {
+ SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
+ .getSubscriptionInfoForSubId(mSubId);
+ CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
+ msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
+ if (info != null) {
+ color = info.getIconTint();
+ }
+ }
+ if (isEsimLocked) {
+ msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
+ }
+
+ if (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) {
+ mSecurityMessageDisplay.setMessage(msg);
+ }
+ mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+ }
+
+ private void showDefaultMessage() {
+ setLockedSimMessage();
+ if (mRemainingAttempts >= 0) {
+ return;
+ }
+
+ // Sending empty PIN here to query the number of remaining PIN attempts
+ new CheckSimPin("", mSubId) {
+ void onSimCheckResponse(final PinResult result) {
+ Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
+ + result.toString());
+ if (result.getAttemptsRemaining() >= 0) {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ setLockedSimMessage();
+ }
+ }
+ }.start();
+ }
+
+ private void handleSubInfoChangeIfNeeded() {
+ KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
+ int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
+ if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubId = subId;
+ mShowDefaultMessage = true;
+ mRemainingAttempts = -1;
+ }
}
@Override
@@ -54,6 +173,35 @@
return 0;
}
+ private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
+ String displayMessage;
+ int msgId;
+ if (attemptsRemaining == 0) {
+ displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
+ } else if (attemptsRemaining > 0) {
+ msgId = isDefault ? R.plurals.kg_password_default_pin_message :
+ R.plurals.kg_password_wrong_pin_code;
+ displayMessage = getContext().getResources()
+ .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
+ } else {
+ msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
+ displayMessage = getContext().getString(msgId);
+ }
+ if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
+ displayMessage = getResources()
+ .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
+ }
+ if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
+ + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
+ return displayMessage;
+ }
+
+ @Override
+ protected boolean shouldLockout(long deadline) {
+ // SIM PIN doesn't have a timed lockout
+ return false;
+ }
+
@Override
protected int getPasswordTextViewId() {
return R.id.simPinEntry;
@@ -66,6 +214,173 @@
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
+ mSimImageView = findViewById(R.id.keyguard_sim);
+ }
+
+ @Override
+ public void showUsabilityHint() {
+
+ }
+
+ @Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
+ resetState();
+ }
+
+ @Override
+ public void onPause() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPin extends Thread {
+ private final String mPin;
+ private int mSubId;
+
+ protected CheckSimPin(String pin, int subId) {
+ mPin = pin;
+ mSubId = subId;
+ }
+
+ abstract void onSimCheckResponse(@NonNull PinResult result);
+
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
+ }
+ TelephonyManager telephonyManager =
+ ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .createForSubscriptionId(mSubId);
+ final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
+ if (result == null) {
+ Log.e(TAG, "Error result for supplyPinReportResult.");
+ post(new Runnable() {
+ @Override
+ public void run() {
+ onSimCheckResponse(PinResult.getDefaultFailedResult());
+ }
+ });
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
+ }
+ post(new Runnable() {
+ @Override
+ public void run() {
+ onSimCheckResponse(result);
+ }
+ });
+ }
+ }
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mContext);
+ mSimUnlockProgressDialog.setMessage(
+ mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ private Dialog getSimRemainingAttemptsDialog(int remaining) {
+ String msg = getPinPasswordErrorMessage(remaining, false);
+ if (mRemainingAttemptsDialog == null) {
+ Builder builder = new AlertDialog.Builder(mContext);
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ String entry = mPasswordEntry.getText();
+
+ if (entry.length() < 4) {
+ // otherwise, display a message to the user, and don't submit.
+ mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint);
+ resetPasswordText(true /* animate */, true /* announce */);
+ mCallback.userActivity();
+ return;
+ }
+
+ getSimUnlockProgressDialog().show();
+
+ if (mCheckSimPinThread == null) {
+ mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
+ @Override
+ void onSimCheckResponse(final PinResult result) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ resetPasswordText(true /* animate */,
+ /* announce */
+ result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
+ Dependency.get(KeyguardUpdateMonitor.class)
+ .reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ if (mCallback != null) {
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
+ } else {
+ mShowDefaultMessage = false;
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
+ if (result.getAttemptsRemaining() <= 2) {
+ // this is getting critical - show dialog
+ getSimRemainingAttemptsDialog(
+ result.getAttemptsRemaining()).show();
+ } else {
+ // show message
+ mSecurityMessageDisplay.setMessage(
+ getPinPasswordErrorMessage(
+ result.getAttemptsRemaining(), false));
+ }
+ } else {
+ // "PIN operation failed!" - no idea what this was and no way to
+ // find out. :/
+ mSecurityMessageDisplay.setMessage(getContext().getString(
+ R.string.kg_password_pin_failed));
+ }
+ if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ + " CheckSimPin.onSimCheckResponse: " + result
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
+ }
+ mCallback.userActivity();
+ mCheckSimPinThread = null;
+ }
+ });
+ }
+ };
+ mCheckSimPinThread.start();
+ }
}
@Override
@@ -74,6 +389,11 @@
}
@Override
+ public boolean startDisappearAnimation(Runnable finishRunnable) {
+ return false;
+ }
+
+ @Override
public CharSequence getTitle() {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
deleted file mode 100644
index cc8bf4f..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.annotation.NonNull;
-import android.app.AlertDialog;
-import android.app.AlertDialog.Builder;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.telephony.PinResult;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageView;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
-
-public class KeyguardSimPinViewController
- extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
- public static final String TAG = "KeyguardSimPinView";
- private static final String LOG_TAG = "KeyguardSimPinView";
- private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final TelephonyManager mTelephonyManager;
-
- private ProgressDialog mSimUnlockProgressDialog;
- private CheckSimPin mCheckSimPinThread;
- private int mRemainingAttempts;
- // Below flag is set to true during power-up or when a new SIM card inserted on device.
- // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
- // be displayed to inform user about the number of remaining PIN attempts left.
- private boolean mShowDefaultMessage;
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private AlertDialog mRemainingAttemptsDialog;
- private ImageView mSimImageView;
-
- KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- if (simState == TelephonyManager.SIM_STATE_READY) {
- mRemainingAttempts = -1;
- resetState();
- } else {
- resetState();
- }
- }
- };
-
- protected KeyguardSimPinViewController(KeyguardSimPinView view,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- SecurityMode securityMode, LockPatternUtils lockPatternUtils,
- KeyguardSecurityCallback keyguardSecurityCallback,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- LatencyTracker latencyTracker,
- LiftToActivateListener liftToActivateListener,
- TelephonyManager telephonyManager) {
- super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
- messageAreaControllerFactory, latencyTracker, liftToActivateListener);
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mTelephonyManager = telephonyManager;
- mSimImageView = mView.findViewById(R.id.keyguard_sim);
- }
-
- @Override
- protected void onViewAttached() {
- super.onViewAttached();
- }
-
- @Override
- void resetState() {
- super.resetState();
- if (DEBUG) Log.v(TAG, "Resetting state");
- handleSubInfoChangeIfNeeded();
- mMessageAreaController.setMessage("");
- if (mShowDefaultMessage) {
- showDefaultMessage();
- }
-
- mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
- }
-
- @Override
- public boolean startDisappearAnimation(Runnable finishRunnable) {
- return false;
- }
-
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
- mView.resetState();
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
-
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- String entry = mPasswordEntry.getText();
-
- if (entry.length() < 4) {
- // otherwise, display a message to the user, and don't submit.
- mMessageAreaController.setMessage(
- com.android.systemui.R.string.kg_invalid_sim_pin_hint);
- mView.resetPasswordText(true /* animate */, true /* announce */);
- getKeyguardSecurityCallback().userActivity();
- return;
- }
-
- getSimUnlockProgressDialog().show();
-
- if (mCheckSimPinThread == null) {
- mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) {
- @Override
- void onSimCheckResponse(final PinResult result) {
- mView.post(() -> {
- mRemainingAttempts = result.getAttemptsRemaining();
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- mView.resetPasswordText(true /* animate */,
- /* announce */
- result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
- if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
- mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser());
- } else {
- mShowDefaultMessage = false;
- if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
- if (result.getAttemptsRemaining() <= 2) {
- // this is getting critical - show dialog
- getSimRemainingAttemptsDialog(
- result.getAttemptsRemaining()).show();
- } else {
- // show message
- mMessageAreaController.setMessage(
- getPinPasswordErrorMessage(
- result.getAttemptsRemaining(), false));
- }
- } else {
- // "PIN operation failed!" - no idea what this was and no way to
- // find out. :/
- mMessageAreaController.setMessage(mView.getResources().getString(
- R.string.kg_password_pin_failed));
- }
- if (DEBUG) {
- Log.d(LOG_TAG, "verifyPasswordAndUnlock "
- + " CheckSimPin.onSimCheckResponse: " + result
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
- }
- getKeyguardSecurityCallback().userActivity();
- mCheckSimPinThread = null;
- });
- }
- };
- mCheckSimPinThread.start();
- }
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
- mSimUnlockProgressDialog.setMessage(
- mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- return mSimUnlockProgressDialog;
- }
-
-
- private Dialog getSimRemainingAttemptsDialog(int remaining) {
- String msg = getPinPasswordErrorMessage(remaining, false);
- if (mRemainingAttemptsDialog == null) {
- Builder builder = new AlertDialog.Builder(mView.getContext());
- builder.setMessage(msg);
- builder.setCancelable(false);
- builder.setNeutralButton(R.string.ok, null);
- mRemainingAttemptsDialog = builder.create();
- mRemainingAttemptsDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- } else {
- mRemainingAttemptsDialog.setMessage(msg);
- }
- return mRemainingAttemptsDialog;
- }
-
-
- private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
- String displayMessage;
- int msgId;
- if (attemptsRemaining == 0) {
- displayMessage = mView.getResources().getString(
- R.string.kg_password_wrong_pin_code_pukked);
- } else if (attemptsRemaining > 0) {
- msgId = isDefault ? R.plurals.kg_password_default_pin_message :
- R.plurals.kg_password_wrong_pin_code;
- displayMessage = mView.getResources()
- .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
- } else {
- msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
- displayMessage = mView.getResources().getString(msgId);
- }
- if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) {
- displayMessage = mView.getResources()
- .getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
- }
- if (DEBUG) {
- Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
- + attemptsRemaining + " displayMessage=" + displayMessage);
- }
- return displayMessage;
- }
-
- private void showDefaultMessage() {
- setLockedSimMessage();
- if (mRemainingAttempts >= 0) {
- return;
- }
-
- // Sending empty PIN here to query the number of remaining PIN attempts
- new CheckSimPin("", mSubId) {
- void onSimCheckResponse(final PinResult result) {
- Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
- + result.toString());
- if (result.getAttemptsRemaining() >= 0) {
- mRemainingAttempts = result.getAttemptsRemaining();
- setLockedSimMessage();
- }
- }
- }.start();
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPin extends Thread {
- private final String mPin;
- private int mSubId;
-
- protected CheckSimPin(String pin, int subId) {
- mPin = pin;
- mSubId = subId;
- }
-
- abstract void onSimCheckResponse(@NonNull PinResult result);
-
- @Override
- public void run() {
- if (DEBUG) {
- Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
- }
- TelephonyManager telephonyManager =
- mTelephonyManager.createForSubscriptionId(mSubId);
- final PinResult result = telephonyManager.supplyPinReportPinResult(mPin);
- if (result == null) {
- Log.e(TAG, "Error result for supplyPinReportResult.");
- mView.post(() -> onSimCheckResponse(PinResult.getDefaultFailedResult()));
- } else {
- if (DEBUG) {
- Log.v(TAG, "supplyPinReportResult returned: " + result.toString());
- }
- mView.post(() -> onSimCheckResponse(result));
- }
- }
- }
-
- private void setLockedSimMessage() {
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
- int count = 1;
- if (mTelephonyManager != null) {
- count = mTelephonyManager.getActiveModemCount();
- }
- Resources rez = mView.getResources();
- String msg;
- TypedArray array = mView.getContext().obtainStyledAttributes(
- new int[] { R.attr.wallpaperTextColor });
- int color = array.getColor(0, Color.WHITE);
- array.recycle();
- if (count < 2) {
- msg = rez.getString(R.string.kg_sim_pin_instructions);
- } else {
- SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId);
- CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
- msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
- if (info != null) {
- color = info.getIconTint();
- }
- }
- if (isEsimLocked) {
- msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
- }
-
- if (mView.getVisibility() == View.VISIBLE) {
- mMessageAreaController.setMessage(msg);
- }
- mSimImageView.setImageTintList(ColorStateList.valueOf(color));
- }
-
- private void handleSubInfoChangeIfNeeded() {
- int subId = mKeyguardUpdateMonitor
- .getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED);
- if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
- mSubId = subId;
- mShowDefaultMessage = true;
- mRemainingAttempts = -1;
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 0d72c93..5148dd7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -16,10 +16,27 @@
package com.android.keyguard;
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.telephony.PinResult;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -27,9 +44,48 @@
* Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
*/
public class KeyguardSimPukView extends KeyguardPinBasedInputView {
+ private static final String LOG_TAG = "KeyguardSimPukView";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
public static final String TAG = "KeyguardSimPukView";
+ private ProgressDialog mSimUnlockProgressDialog = null;
+ private CheckSimPuk mCheckSimPukThread;
+
+ // Below flag is set to true during power-up or when a new SIM card inserted on device.
+ // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
+ // be displayed to inform user about the number of remaining PUK attempts left.
+ private boolean mShowDefaultMessage = true;
+ private int mRemainingAttempts = -1;
+ private String mPukText;
+ private String mPinText;
+ private StateMachine mStateMachine = new StateMachine();
+ private AlertDialog mRemainingAttemptsDialog;
+ private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private ImageView mSimImageView;
+
+ KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onSimStateChanged(int subId, int slotId, int simState) {
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ switch(simState) {
+ // If the SIM is unlocked via a key sequence through the emergency dialer, it will
+ // move into the READY state and the PUK lock keyguard should be removed.
+ case TelephonyManager.SIM_STATE_READY: {
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ // mCallback can be null if onSimStateChanged callback is called when keyguard
+ // isn't active.
+ if (mCallback != null) {
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
+ break;
+ }
+ default:
+ resetState();
+ }
+ }
+ };
+
public KeyguardSimPukView(Context context) {
this(context, null);
}
@@ -38,14 +94,136 @@
super(context, attrs);
}
+ private class StateMachine {
+ final int ENTER_PUK = 0;
+ final int ENTER_PIN = 1;
+ final int CONFIRM_PIN = 2;
+ final int DONE = 3;
+ private int state = ENTER_PUK;
+
+ public void next() {
+ int msg = 0;
+ if (state == ENTER_PUK) {
+ if (checkPuk()) {
+ state = ENTER_PIN;
+ msg = R.string.kg_puk_enter_pin_hint;
+ } else {
+ msg = R.string.kg_invalid_sim_puk_hint;
+ }
+ } else if (state == ENTER_PIN) {
+ if (checkPin()) {
+ state = CONFIRM_PIN;
+ msg = R.string.kg_enter_confirm_pin_hint;
+ } else {
+ msg = R.string.kg_invalid_sim_pin_hint;
+ }
+ } else if (state == CONFIRM_PIN) {
+ if (confirmPin()) {
+ state = DONE;
+ msg = R.string.keyguard_sim_unlock_progress_dialog_message;
+ updateSim();
+ } else {
+ state = ENTER_PIN; // try again?
+ msg = R.string.kg_invalid_confirm_pin_hint;
+ }
+ }
+ resetPasswordText(true /* animate */, true /* announce */);
+ if (msg != 0) {
+ mSecurityMessageDisplay.setMessage(msg);
+ }
+ }
+
+
+ void reset() {
+ mPinText="";
+ mPukText="";
+ state = ENTER_PUK;
+ handleSubInfoChangeIfNeeded();
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
+
+ KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
+ esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
+ mPasswordEntry.requestFocus();
+ }
+
+
+ }
+
+ private void showDefaultMessage() {
+ if (mRemainingAttempts >= 0) {
+ mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
+ mRemainingAttempts, true));
+ return;
+ }
+
+ boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
+ int count = 1;
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ if (telephonyManager != null) {
+ count = telephonyManager.getActiveModemCount();
+ }
+ Resources rez = getResources();
+ String msg;
+ TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor });
+ int color = array.getColor(0, Color.WHITE);
+ array.recycle();
+ if (count < 2) {
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint);
+ } else {
+ SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
+ .getSubscriptionInfoForSubId(mSubId);
+ CharSequence displayName = info != null ? info.getDisplayName() : "";
+ msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
+ if (info != null) {
+ color = info.getIconTint();
+ }
+ }
+ if (isEsimLocked) {
+ msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
+ }
+ if (mSecurityMessageDisplay != null) {
+ mSecurityMessageDisplay.setMessage(msg);
+ }
+ mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+
+ // Sending empty PUK here to query the number of remaining PIN attempts
+ new CheckSimPuk("", "", mSubId) {
+ void onSimLockChangedResponse(final PinResult result) {
+ if (result == null) Log.e(LOG_TAG, "onSimCheckResponse, pin result is NULL");
+ else {
+ Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result "
+ + result.toString());
+ if (result.getAttemptsRemaining() >= 0) {
+ mRemainingAttempts = result.getAttemptsRemaining();
+ mSecurityMessageDisplay.setMessage(
+ getPukPasswordErrorMessage(result.getAttemptsRemaining(), true));
+ }
+ }
+ }
+ }.start();
+ }
+
+ private void handleSubInfoChangeIfNeeded() {
+ KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class);
+ int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED);
+ if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+ mSubId = subId;
+ mShowDefaultMessage = true;
+ mRemainingAttempts = -1;
+ }
+ }
+
@Override
protected int getPromptReasonStringRes(int reason) {
// No message on SIM Puk
return 0;
}
- String getPukPasswordErrorMessage(
- int attemptsRemaining, boolean isDefault, boolean isEsimLocked) {
+ private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
String displayMessage;
if (attemptsRemaining == 0) {
@@ -60,19 +238,28 @@
R.string.kg_password_puk_failed;
displayMessage = getContext().getString(msgId);
}
- if (isEsimLocked) {
+ if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) {
displayMessage = getResources()
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
}
- if (DEBUG) {
- Log.d(TAG, "getPukPasswordErrorMessage:"
- + " attemptsRemaining=" + attemptsRemaining
- + " displayMessage=" + displayMessage);
- }
+ if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
+ + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
return displayMessage;
}
@Override
+ public void resetState() {
+ super.resetState();
+ mStateMachine.reset();
+ }
+
+ @Override
+ protected boolean shouldLockout(long deadline) {
+ // SIM PUK doesn't have a timed lockout
+ return false;
+ }
+
+ @Override
protected int getPasswordTextViewId() {
return R.id.pukEntry;
}
@@ -84,6 +271,197 @@
if (mEcaView instanceof EmergencyCarrierArea) {
((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true);
}
+ mSimImageView = findViewById(R.id.keyguard_sim);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback);
+ resetState();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback);
+ }
+
+ @Override
+ public void showUsabilityHint() {
+ }
+
+ @Override
+ public void onPause() {
+ // dismiss the dialog.
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.dismiss();
+ mSimUnlockProgressDialog = null;
+ }
+ }
+
+ /**
+ * Since the IPC can block, we want to run the request in a separate thread
+ * with a callback.
+ */
+ private abstract class CheckSimPuk extends Thread {
+
+ private final String mPin, mPuk;
+ private final int mSubId;
+
+ protected CheckSimPuk(String puk, String pin, int subId) {
+ mPuk = puk;
+ mPin = pin;
+ mSubId = subId;
+ }
+
+ abstract void onSimLockChangedResponse(@NonNull PinResult result);
+
+ @Override
+ public void run() {
+ if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
+ TelephonyManager telephonyManager =
+ ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .createForSubscriptionId(mSubId);
+ final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
+ if (result == null) {
+ Log.e(TAG, "Error result for supplyPukReportResult.");
+ post(new Runnable() {
+ @Override
+ public void run() {
+ onSimLockChangedResponse(PinResult.getDefaultFailedResult());
+ }
+ });
+ } else {
+ if (DEBUG) {
+ Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
+ }
+ post(new Runnable() {
+ @Override
+ public void run() {
+ onSimLockChangedResponse(result);
+ }
+ });
+ }
+ }
+ }
+
+ private Dialog getSimUnlockProgressDialog() {
+ if (mSimUnlockProgressDialog == null) {
+ mSimUnlockProgressDialog = new ProgressDialog(mContext);
+ mSimUnlockProgressDialog.setMessage(
+ mContext.getString(R.string.kg_sim_unlock_progress_dialog_message));
+ mSimUnlockProgressDialog.setIndeterminate(true);
+ mSimUnlockProgressDialog.setCancelable(false);
+ if (!(mContext instanceof Activity)) {
+ mSimUnlockProgressDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ }
+ }
+ return mSimUnlockProgressDialog;
+ }
+
+ private Dialog getPukRemainingAttemptsDialog(int remaining) {
+ String msg = getPukPasswordErrorMessage(remaining, false);
+ if (mRemainingAttemptsDialog == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ builder.setMessage(msg);
+ builder.setCancelable(false);
+ builder.setNeutralButton(R.string.ok, null);
+ mRemainingAttemptsDialog = builder.create();
+ mRemainingAttemptsDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ } else {
+ mRemainingAttemptsDialog.setMessage(msg);
+ }
+ return mRemainingAttemptsDialog;
+ }
+
+ private boolean checkPuk() {
+ // make sure the puk is at least 8 digits long.
+ if (mPasswordEntry.getText().length() == 8) {
+ mPukText = mPasswordEntry.getText();
+ return true;
+ }
+ return false;
+ }
+
+ private boolean checkPin() {
+ // make sure the PIN is between 4 and 8 digits
+ int length = mPasswordEntry.getText().length();
+ if (length >= 4 && length <= 8) {
+ mPinText = mPasswordEntry.getText();
+ return true;
+ }
+ return false;
+ }
+
+ public boolean confirmPin() {
+ return mPinText.equals(mPasswordEntry.getText());
+ }
+
+ private void updateSim() {
+ getSimUnlockProgressDialog().show();
+
+ if (mCheckSimPukThread == null) {
+ mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
+ @Override
+ void onSimLockChangedResponse(final PinResult result) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (mSimUnlockProgressDialog != null) {
+ mSimUnlockProgressDialog.hide();
+ }
+ resetPasswordText(true /* animate */,
+ /* announce */
+ result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
+ Dependency.get(KeyguardUpdateMonitor.class)
+ .reportSimUnlocked(mSubId);
+ mRemainingAttempts = -1;
+ mShowDefaultMessage = true;
+ if (mCallback != null) {
+ mCallback.dismiss(true,
+ KeyguardUpdateMonitor.getCurrentUser());
+ }
+ } else {
+ mShowDefaultMessage = false;
+ if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
+ // show message
+ mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), false));
+ if (result.getAttemptsRemaining() <= 2) {
+ // this is getting critical - show dialog
+ getPukRemainingAttemptsDialog(
+ result.getAttemptsRemaining()).show();
+ } else {
+ // show message
+ mSecurityMessageDisplay.setMessage(
+ getPukPasswordErrorMessage(
+ result.getAttemptsRemaining(), false));
+ }
+ } else {
+ mSecurityMessageDisplay.setMessage(getContext().getString(
+ R.string.kg_password_puk_failed));
+ }
+ if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ + " UpdateSim.onSimCheckResponse: "
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
+ }
+ mStateMachine.reset();
+ mCheckSimPukThread = null;
+ }
+ });
+ }
+ };
+ mCheckSimPukThread.start();
+ }
+ }
+
+ @Override
+ protected void verifyPasswordAndUnlock() {
+ mStateMachine.next();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
deleted file mode 100644
index a873749..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.telephony.PinResult;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.ImageView;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-
-public class KeyguardSimPukViewController
- extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
- private static final boolean DEBUG = KeyguardConstants.DEBUG;
- public static final String TAG = "KeyguardSimPukView";
-
- private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- private final TelephonyManager mTelephonyManager;
-
- private String mPukText;
- private String mPinText;
- private int mRemainingAttempts;
- // Below flag is set to true during power-up or when a new SIM card inserted on device.
- // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
- // be displayed to inform user about the number of remaining PUK attempts left.
- private boolean mShowDefaultMessage;
- private StateMachine mStateMachine = new StateMachine();
- private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private CheckSimPuk mCheckSimPukThread;
- private ProgressDialog mSimUnlockProgressDialog;
-
- KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
- @Override
- public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- // If the SIM is unlocked via a key sequence through the emergency dialer, it will
- // move into the READY state and the PUK lock keyguard should be removed.
- if (simState == TelephonyManager.SIM_STATE_READY) {
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
- getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
- } else {
- resetState();
- }
- }
- };
- private ImageView mSimImageView;
- private AlertDialog mRemainingAttemptsDialog;
-
- protected KeyguardSimPukViewController(KeyguardSimPukView view,
- KeyguardUpdateMonitor keyguardUpdateMonitor,
- SecurityMode securityMode, LockPatternUtils lockPatternUtils,
- KeyguardSecurityCallback keyguardSecurityCallback,
- KeyguardMessageAreaController.Factory messageAreaControllerFactory,
- LatencyTracker latencyTracker,
- LiftToActivateListener liftToActivateListener,
- TelephonyManager telephonyManager) {
- super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
- messageAreaControllerFactory, latencyTracker, liftToActivateListener);
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mTelephonyManager = telephonyManager;
- mSimImageView = mView.findViewById(R.id.keyguard_sim);
- }
-
- @Override
- protected void onViewAttached() {
- super.onViewAttached();
- mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
- }
-
- @Override
- protected void onViewDetached() {
- super.onViewDetached();
- mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
- }
-
- @Override
- void resetState() {
- super.resetState();
- mStateMachine.reset();
- }
-
- @Override
- protected void verifyPasswordAndUnlock() {
- mStateMachine.next();
- }
-
- private class StateMachine {
- static final int ENTER_PUK = 0;
- static final int ENTER_PIN = 1;
- static final int CONFIRM_PIN = 2;
- static final int DONE = 3;
-
- private int mState = ENTER_PUK;
-
- public void next() {
- int msg = 0;
- if (mState == ENTER_PUK) {
- if (checkPuk()) {
- mState = ENTER_PIN;
- msg = com.android.systemui.R.string.kg_puk_enter_pin_hint;
- } else {
- msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint;
- }
- } else if (mState == ENTER_PIN) {
- if (checkPin()) {
- mState = CONFIRM_PIN;
- msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint;
- } else {
- msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint;
- }
- } else if (mState == CONFIRM_PIN) {
- if (confirmPin()) {
- mState = DONE;
- msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message;
- updateSim();
- } else {
- mState = ENTER_PIN; // try again?
- msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint;
- }
- }
- mView.resetPasswordText(true /* animate */, true /* announce */);
- if (msg != 0) {
- mMessageAreaController.setMessage(msg);
- }
- }
-
-
- void reset() {
- mPinText = "";
- mPukText = "";
- mState = ENTER_PUK;
- handleSubInfoChangeIfNeeded();
- if (mShowDefaultMessage) {
- showDefaultMessage();
- }
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
-
- KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
- mPasswordEntry.requestFocus();
- }
- }
-
- private void showDefaultMessage() {
- if (mRemainingAttempts >= 0) {
- mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
- mRemainingAttempts, true,
- KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
- return;
- }
-
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
- int count = 1;
- if (mTelephonyManager != null) {
- count = mTelephonyManager.getActiveModemCount();
- }
- Resources rez = mView.getResources();
- String msg;
- TypedArray array = mView.getContext().obtainStyledAttributes(
- new int[] { R.attr.wallpaperTextColor });
- int color = array.getColor(0, Color.WHITE);
- array.recycle();
- if (count < 2) {
- msg = rez.getString(R.string.kg_puk_enter_puk_hint);
- } else {
- SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class)
- .getSubscriptionInfoForSubId(mSubId);
- CharSequence displayName = info != null ? info.getDisplayName() : "";
- msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
- if (info != null) {
- color = info.getIconTint();
- }
- }
- if (isEsimLocked) {
- msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg);
- }
- mMessageAreaController.setMessage(msg);
- mSimImageView.setImageTintList(ColorStateList.valueOf(color));
-
- // Sending empty PUK here to query the number of remaining PIN attempts
- new CheckSimPuk("", "", mSubId) {
- void onSimLockChangedResponse(final PinResult result) {
- if (result == null) Log.e(TAG, "onSimCheckResponse, pin result is NULL");
- else {
- Log.d(TAG, "onSimCheckResponse " + " empty One result "
- + result.toString());
- if (result.getAttemptsRemaining() >= 0) {
- mRemainingAttempts = result.getAttemptsRemaining();
- mMessageAreaController.setMessage(
- mView.getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), true,
- KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
- }
- }
- }
- }.start();
- }
-
- private boolean checkPuk() {
- // make sure the puk is at least 8 digits long.
- if (mPasswordEntry.getText().length() == 8) {
- mPukText = mPasswordEntry.getText();
- return true;
- }
- return false;
- }
-
- private boolean checkPin() {
- // make sure the PIN is between 4 and 8 digits
- int length = mPasswordEntry.getText().length();
- if (length >= 4 && length <= 8) {
- mPinText = mPasswordEntry.getText();
- return true;
- }
- return false;
- }
-
- public boolean confirmPin() {
- return mPinText.equals(mPasswordEntry.getText());
- }
-
-
-
-
- private void updateSim() {
- getSimUnlockProgressDialog().show();
-
- if (mCheckSimPukThread == null) {
- mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) {
- @Override
- void onSimLockChangedResponse(final PinResult result) {
- mView.post(() -> {
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.hide();
- }
- mView.resetPasswordText(true /* animate */,
- /* announce */
- result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS);
- if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) {
- mKeyguardUpdateMonitor.reportSimUnlocked(mSubId);
- mRemainingAttempts = -1;
- mShowDefaultMessage = true;
-
- getKeyguardSecurityCallback().dismiss(
- true, KeyguardUpdateMonitor.getCurrentUser());
- } else {
- mShowDefaultMessage = false;
- if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) {
- // show message
- mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), false,
- KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)));
- if (result.getAttemptsRemaining() <= 2) {
- // this is getting critical - show dialog
- getPukRemainingAttemptsDialog(
- result.getAttemptsRemaining()).show();
- } else {
- // show message
- mMessageAreaController.setMessage(
- mView.getPukPasswordErrorMessage(
- result.getAttemptsRemaining(), false,
- KeyguardEsimArea.isEsimLocked(
- mView.getContext(), mSubId)));
- }
- } else {
- mMessageAreaController.setMessage(mView.getResources().getString(
- R.string.kg_password_puk_failed));
- }
- if (DEBUG) {
- Log.d(TAG, "verifyPasswordAndUnlock "
- + " UpdateSim.onSimCheckResponse: "
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
- }
- mStateMachine.reset();
- mCheckSimPukThread = null;
- });
- }
- };
- mCheckSimPukThread.start();
- }
- }
-
- @Override
- protected boolean shouldLockout(long deadline) {
- // SIM PUK doesn't have a timed lockout
- return false;
- }
-
- private Dialog getSimUnlockProgressDialog() {
- if (mSimUnlockProgressDialog == null) {
- mSimUnlockProgressDialog = new ProgressDialog(mView.getContext());
- mSimUnlockProgressDialog.setMessage(
- mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message));
- mSimUnlockProgressDialog.setIndeterminate(true);
- mSimUnlockProgressDialog.setCancelable(false);
- if (!(mView.getContext() instanceof Activity)) {
- mSimUnlockProgressDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- }
- }
- return mSimUnlockProgressDialog;
- }
-
- private void handleSubInfoChangeIfNeeded() {
- int subId = mKeyguardUpdateMonitor.getNextSubIdForState(
- TelephonyManager.SIM_STATE_PUK_REQUIRED);
- if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
- mSubId = subId;
- mShowDefaultMessage = true;
- mRemainingAttempts = -1;
- }
- }
-
-
- private Dialog getPukRemainingAttemptsDialog(int remaining) {
- String msg = mView.getPukPasswordErrorMessage(remaining, false,
- KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId));
- if (mRemainingAttemptsDialog == null) {
- AlertDialog.Builder builder = new AlertDialog.Builder(mView.getContext());
- builder.setMessage(msg);
- builder.setCancelable(false);
- builder.setNeutralButton(R.string.ok, null);
- mRemainingAttemptsDialog = builder.create();
- mRemainingAttemptsDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- } else {
- mRemainingAttemptsDialog.setMessage(msg);
- }
- return mRemainingAttemptsDialog;
- }
-
- @Override
- public void onPause() {
- // dismiss the dialog.
- if (mSimUnlockProgressDialog != null) {
- mSimUnlockProgressDialog.dismiss();
- mSimUnlockProgressDialog = null;
- }
- }
-
- /**
- * Since the IPC can block, we want to run the request in a separate thread
- * with a callback.
- */
- private abstract class CheckSimPuk extends Thread {
-
- private final String mPin, mPuk;
- private final int mSubId;
-
- protected CheckSimPuk(String puk, String pin, int subId) {
- mPuk = puk;
- mPin = pin;
- mSubId = subId;
- }
-
- abstract void onSimLockChangedResponse(@NonNull PinResult result);
-
- @Override
- public void run() {
- if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
- TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
- final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin);
- if (result == null) {
- Log.e(TAG, "Error result for supplyPukReportResult.");
- mView.post(() -> onSimLockChangedResponse(PinResult.getDefaultFailedResult()));
- } else {
- if (DEBUG) {
- Log.v(TAG, "supplyPukReportResult returned: " + result.toString());
- }
- mView.post(new Runnable() {
- @Override
- public void run() {
- onSimLockChangedResponse(result);
- }
- });
- }
- }
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
index 425e50e..e59602b 100644
--- a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
+++ b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java
@@ -16,12 +16,11 @@
package com.android.keyguard;
+import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
-import javax.inject.Inject;
-
/**
* Hover listener that implements lift-to-activate interaction for
* accessibility. May be added to multiple views.
@@ -32,9 +31,9 @@
private boolean mCachedClickableState;
- @Inject
- LiftToActivateListener(AccessibilityManager accessibilityManager) {
- mAccessibilityManager = accessibilityManager;
+ public LiftToActivateListener(Context context) {
+ mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 2205fdd..b0457fc 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -26,7 +26,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import com.android.internal.widget.LockPatternUtils;
@@ -91,8 +90,7 @@
}
setOnClickListener(mListener);
- setOnHoverListener(new LiftToActivateListener(
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE)));
+ setOnHoverListener(new LiftToActivateListener(context));
mLockPatternUtils = new LockPatternUtils(context);
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index 8811088..b6010c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -22,7 +22,6 @@
import com.android.keyguard.KeyguardHostView;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSecurityContainer;
-import com.android.keyguard.KeyguardSecurityViewFlipper;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -59,15 +58,7 @@
/** */
@Provides
@KeyguardBouncerScope
- static KeyguardSecurityContainer providesKeyguardSecurityContainer(KeyguardHostView hostView) {
+ static KeyguardSecurityContainer preovidesKeyguardSecurityContainer(KeyguardHostView hostView) {
return hostView.findViewById(R.id.keyguard_security_container);
}
-
- /** */
- @Provides
- @KeyguardBouncerScope
- static KeyguardSecurityViewFlipper providesKeyguardSecurityViewFlipper(
- KeyguardSecurityContainer containerView) {
- return containerView.findViewById(R.id.view_flipper);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
index 1b2e4c6..02a672b 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleReminderExpBehavior.java
@@ -26,6 +26,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
@@ -66,8 +68,10 @@
@SysUISingleton
final class AssistHandleReminderExpBehavior implements BehaviorController {
- private static final String LEARNING_TIME_ELAPSED_KEY = "reminder_exp_learning_time_elapsed";
- private static final String LEARNING_EVENT_COUNT_KEY = "reminder_exp_learning_event_count";
+ private static final Uri LEARNING_TIME_ELAPSED_URI =
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS);
+ private static final Uri LEARNING_EVENT_COUNT_URI =
+ Settings.Secure.getUriFor(Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT);
private static final String LEARNED_HINT_LAST_SHOWN_KEY =
"reminder_exp_learned_hint_last_shown";
private static final long DEFAULT_LEARNING_TIME_MS = TimeUnit.DAYS.toMillis(10);
@@ -181,6 +185,7 @@
private boolean mIsNavBarHidden;
private boolean mIsLauncherShowing;
private int mConsecutiveTaskSwitches;
+ @Nullable private ContentObserver mSettingObserver;
/** Whether user has learned the gesture. */
private boolean mIsLearned;
@@ -248,9 +253,22 @@
mWakefulnessLifecycle.get().addObserver(mWakefulnessLifecycleObserver);
mLearningTimeElapsed = Settings.Secure.getLong(
- context.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, /* default = */ 0);
+ context.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ /* default = */ 0);
mLearningCount = Settings.Secure.getInt(
- context.getContentResolver(), LEARNING_EVENT_COUNT_KEY, /* default = */ 0);
+ context.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ /* default = */ 0);
+ mSettingObserver = new SettingsObserver(context, mHandler);
+ context.getContentResolver().registerContentObserver(
+ LEARNING_TIME_ELAPSED_URI,
+ /* notifyForDescendants = */ true,
+ mSettingObserver);
+ context.getContentResolver().registerContentObserver(
+ LEARNING_EVENT_COUNT_URI,
+ /* notifyForDescendants = */ true,
+ mSettingObserver);
mLearnedHintLastShownEpochDay = Settings.Secure.getLong(
context.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, /* default = */ 0);
mLastLearningTimestamp = mClock.currentTimeMillis();
@@ -264,8 +282,20 @@
if (mContext != null) {
mBroadcastDispatcher.get().unregisterReceiver(mDefaultHomeBroadcastReceiver);
mBootCompleteCache.get().removeListener(mBootCompleteListener);
- Settings.Secure.putLong(mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, 0);
- Settings.Secure.putInt(mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, 0);
+ mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
+ mSettingObserver = null;
+ // putString in order to use overrideableByRestore
+ Settings.Secure.putString(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ Long.toString(0L),
+ /* overrideableByRestore = */ true);
+ // putString in order to use overrideableByRestore
+ Settings.Secure.putString(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ Integer.toString(0),
+ /* overrideableByRestore = */ true);
Settings.Secure.putLong(mContext.getContentResolver(), LEARNED_HINT_LAST_SHOWN_KEY, 0);
mContext = null;
}
@@ -282,8 +312,12 @@
return;
}
- Settings.Secure.putLong(
- mContext.getContentResolver(), LEARNING_EVENT_COUNT_KEY, ++mLearningCount);
+ // putString in order to use overrideableByRestore
+ Settings.Secure.putString(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ Integer.toString(++mLearningCount),
+ /* overrideableByRestore = */ true);
}
@Override
@@ -460,8 +494,12 @@
mIsLearned =
mLearningCount >= getLearningCount() || mLearningTimeElapsed >= getLearningTimeMs();
- mHandler.post(() -> Settings.Secure.putLong(
- mContext.getContentResolver(), LEARNING_TIME_ELAPSED_KEY, mLearningTimeElapsed));
+ // putString in order to use overrideableByRestore
+ mHandler.post(() -> Settings.Secure.putString(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ Long.toString(mLearningTimeElapsed),
+ /* overrideableByRestore = */ true));
}
private void resetConsecutiveTaskSwitches() {
@@ -589,4 +627,32 @@
+ "="
+ getShowWhenTaught());
}
+
+ private final class SettingsObserver extends ContentObserver {
+
+ private final Context mContext;
+
+ SettingsObserver(Context context, Handler handler) {
+ super(handler);
+ mContext = context;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, @Nullable Uri uri) {
+ if (LEARNING_TIME_ELAPSED_URI.equals(uri)) {
+ mLastLearningTimestamp = mClock.currentTimeMillis();
+ mLearningTimeElapsed = Settings.Secure.getLong(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
+ /* default = */ 0);
+ } else if (LEARNING_EVENT_COUNT_URI.equals(uri)) {
+ mLearningCount = Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
+ /* default = */ 0);
+ }
+
+ super.onChange(selfChange, uri);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index bde9a6e..38a191c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -309,7 +309,7 @@
final List<FingerprintSensorProperties> fingerprintSensorProperties =
mFingerprintManager.getSensorProperties();
for (FingerprintSensorProperties props : fingerprintSensorProperties) {
- if (props.sensorType == FingerprintSensorProperties.TYPE_UDFPS) {
+ if (props.isAnyUdfpsType()) {
mUdfpsController = mUdfpsControllerFactory.get();
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index fbb47e2..5b85208 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -294,6 +294,15 @@
}
/**
+ * Sets whether this bubble is considered visually interruptive. Normally pulled from the
+ * {@link NotificationEntry}, this method is purely for testing.
+ */
+ @VisibleForTesting
+ void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) {
+ mIsVisuallyInterruptive = visuallyInterruptive;
+ }
+
+ /**
* Starts a task to inflate & load any necessary information to display a bubble.
*
* @param callback the callback to notify one the bubble is ready to be displayed.
@@ -419,6 +428,7 @@
} else if (mIntent != null && entry.getBubbleMetadata().getIntent() == null) {
// Was an intent bubble now it's a shortcut bubble... still unregister the listener
mIntent.unregisterCancelListener(mIntentCancelListener);
+ mIntentActive = false;
mIntent = null;
}
mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 57672c6..dff405c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -90,7 +90,6 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PinnedStackListenerForwarder;
import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -114,6 +113,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -366,6 +366,7 @@
INotificationManager notificationManager,
@Nullable IStatusBarService statusBarService,
WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps) {
dumpManager.registerDumpable(TAG, this);
mContext = context;
@@ -414,7 +415,8 @@
if (bubble.getBubbleIntent() == null) {
return;
}
- if (bubble.isIntentActive()) {
+ if (bubble.isIntentActive()
+ || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
bubble.setPendingIntentCanceled();
return;
}
@@ -441,7 +443,7 @@
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(new BubblesImeListener());
+ windowManagerShellWrapper.addPinnedStackListener(new BubblesImeListener());
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -1112,8 +1114,17 @@
if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) {
notif.setInterruption();
}
- Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
- inflateAndAdd(bubble, suppressFlyout, showInShade);
+ if (!notif.getRanking().visuallyInterruptive()
+ && (notif.getBubbleMetadata() != null
+ && !notif.getBubbleMetadata().getAutoExpandBubble())
+ && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
+ // Update the bubble but don't promote it out of overflow
+ Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
+ b.setEntry(notif);
+ } else {
+ Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
+ inflateAndAdd(bubble, suppressFlyout, showInShade);
+ }
}
void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index bab18ec..2c3cb5f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -284,7 +284,8 @@
} else {
// Updates an existing bubble
bubble.setSuppressFlyout(suppressFlyout);
- doUpdate(bubble);
+ // If there is no flyout, we probably shouldn't show the bubble at the top
+ doUpdate(bubble, !suppressFlyout /* reorder */);
}
if (bubble.shouldAutoExpand()) {
@@ -438,12 +439,12 @@
}
}
- private void doUpdate(Bubble bubble) {
+ private void doUpdate(Bubble bubble, boolean reorder) {
if (DEBUG_BUBBLE_DATA) {
Log.d(TAG, "doUpdate: " + bubble);
}
mStateChange.updatedBubble = bubble;
- if (!isExpanded()) {
+ if (!isExpanded() && reorder) {
int prevPos = mBubbles.indexOf(bubble);
mBubbles.remove(bubble);
mBubbles.add(0, bubble);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index b708d38..08902f8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -41,6 +41,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import dagger.Module;
import dagger.Provides;
@@ -74,6 +75,7 @@
INotificationManager notifManager,
IStatusBarService statusBarService,
WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps) {
return new BubbleController(
context,
@@ -97,6 +99,7 @@
notifManager,
statusBarService,
windowManager,
+ windowManagerShellWrapper,
launcherApps);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index eb43127..c90e6b1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -66,6 +66,7 @@
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
+import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.phone.AutoHideController;
@@ -288,7 +289,6 @@
/** */
@Provides
- @SysUISingleton
public LockPatternUtils provideLockPatternUtils(Context context) {
return new LockPatternUtils(context);
}
@@ -313,6 +313,13 @@
/** */
@Provides
+ public WindowManagerWrapper providesWindowManagerWrapper() {
+ return WindowManagerWrapper.getInstance();
+ }
+
+ /** */
+ @Provides
+ @SysUISingleton
public SystemActions providesSystemActions(Context context) {
return new SystemActions(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 79925ba..b35579d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -62,7 +62,6 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
-import android.view.inputmethod.InputMethodManager;
import com.android.internal.app.IBatteryStats;
import com.android.internal.statusbar.IStatusBarService;
@@ -184,12 +183,6 @@
@Provides
@Singleton
- static InputMethodManager provideInputMethodManager(Context context) {
- return context.getSystemService(InputMethodManager.class);
- }
-
- @Provides
- @Singleton
static IPackageManager provideIPackageManager() {
return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java
new file mode 100644
index 0000000..aca033e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaBrowserFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.browse.MediaBrowser;
+import android.os.Bundle;
+
+import javax.inject.Inject;
+
+/**
+ * Testable wrapper around {@link MediaBrowser} constructor
+ */
+public class MediaBrowserFactory {
+ private final Context mContext;
+
+ @Inject
+ public MediaBrowserFactory(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Creates a new MediaBrowser
+ *
+ * @param serviceComponent
+ * @param callback
+ * @param rootHints
+ * @return
+ */
+ public MediaBrowser create(ComponentName serviceComponent,
+ MediaBrowser.ConnectionCallback callback, Bundle rootHints) {
+ return new MediaBrowser(mContext, serviceComponent, callback, rootHints);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index f150381..1beb875 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -174,7 +174,7 @@
mediaManager.addListener(object : MediaDataManager.Listener {
override fun onMediaDataLoaded(key: String, oldKey: String?, data: MediaData) {
addOrUpdatePlayer(key, oldKey, data)
- val canRemove = data.isPlaying?.let { !it } ?: data.isClearable
+ val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) {
// This view isn't playing, let's remove this! This happens e.g when
// dismissing/timing out a view. We still have the data around because
@@ -250,13 +250,13 @@
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
newPlayer.view?.player?.setLayoutParams(lp)
- newPlayer.bind(data)
+ newPlayer.bind(data, key)
newPlayer.setListening(currentlyExpanded)
MediaPlayerData.addMediaPlayer(key, data, newPlayer)
updatePlayerToState(newPlayer, noAnimation = true)
reorderAllPlayers()
} else {
- existingPlayer.bind(data)
+ existingPlayer.bind(data, key)
MediaPlayerData.addMediaPlayer(key, data, existingPlayer)
if (visualStabilityManager.isReorderingAllowed) {
reorderAllPlayers()
@@ -274,7 +274,7 @@
}
}
- private fun removePlayer(key: String) {
+ private fun removePlayer(key: String, dismissMediaData: Boolean = true) {
val removed = MediaPlayerData.removeMediaPlayer(key)
removed?.apply {
mediaCarouselScrollHandler.onPrePlayerRemoved(removed)
@@ -283,13 +283,16 @@
mediaCarouselScrollHandler.onPlayersChanged()
updatePageIndicator()
- // Inform the media manager of a potentially late dismissal
- mediaManager.dismissMediaData(key, 0L)
+ if (dismissMediaData) {
+ // Inform the media manager of a potentially late dismissal
+ mediaManager.dismissMediaData(key, 0L)
+ }
}
}
private fun recreatePlayers() {
MediaPlayerData.mediaData().forEach { (key, data) ->
+ removePlayer(key, dismissMediaData = false)
addOrUpdatePlayer(key = key, oldKey = null, data = data)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index e55678dc..810cecc 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -82,6 +82,7 @@
private Context mContext;
private PlayerViewHolder mViewHolder;
+ private String mKey;
private MediaViewController mMediaViewController;
private MediaSession.Token mToken;
private MediaController mController;
@@ -206,10 +207,11 @@
/**
* Bind this view based on the data given
*/
- public void bind(@NonNull MediaData data) {
+ public void bind(@NonNull MediaData data, String key) {
if (mViewHolder == null) {
return;
}
+ mKey = key;
MediaSession.Token token = data.getToken();
mBackgroundColor = data.getBackgroundColor();
if (mToken == null || !mToken.equals(token)) {
@@ -359,10 +361,10 @@
// Dismiss
mViewHolder.getDismiss().setOnClickListener(v -> {
- if (data.getNotificationKey() != null) {
+ if (mKey != null) {
closeGuts();
mKeyguardDismissUtil.executeWhenUnlocked(() -> {
- mMediaDataManagerLazy.get().dismissMediaData(data.getNotificationKey(),
+ mMediaDataManagerLazy.get().dismissMediaData(mKey,
MediaViewController.GUTS_ANIMATION_DURATION + 100);
return true;
}, /* requiresShadeOpen */ true);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
index 2bc908b..a993d00 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt
@@ -16,7 +16,6 @@
package com.android.systemui.media
-import android.content.Context
import android.media.MediaRouter2Manager
import android.media.session.MediaController
import androidx.annotation.AnyThread
@@ -25,7 +24,6 @@
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.systemui.Dumpable
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -34,11 +32,13 @@
import java.util.concurrent.Executor
import javax.inject.Inject
+private const val PLAYBACK_TYPE_UNKNOWN = 0
+
/**
* Provides information about the route (ie. device) where playback is occurring.
*/
class MediaDeviceManager @Inject constructor(
- private val context: Context,
+ private val controllerFactory: MediaControllerFactory,
private val localMediaManagerFactory: LocalMediaManagerFactory,
private val mr2manager: MediaRouter2Manager,
@Main private val fgExecutor: Executor,
@@ -72,7 +72,7 @@
if (entry == null || entry?.token != data.token) {
entry?.stop()
val controller = data.token?.let {
- MediaController(context, it)
+ controllerFactory.create(it)
}
entry = Entry(key, oldKey, controller,
localMediaManagerFactory.create(data.packageName))
@@ -123,11 +123,12 @@
val oldKey: String?,
val controller: MediaController?,
val localMediaManager: LocalMediaManager
- ) : LocalMediaManager.DeviceCallback {
+ ) : LocalMediaManager.DeviceCallback, MediaController.Callback() {
val token
get() = controller?.sessionToken
private var started = false
+ private var playbackType = PLAYBACK_TYPE_UNKNOWN
private var current: MediaDevice? = null
set(value) {
if (!started || value != field) {
@@ -142,6 +143,8 @@
fun start() = bgExecutor.execute {
localMediaManager.registerCallback(this)
localMediaManager.startScan()
+ playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
+ controller?.registerCallback(this)
updateCurrent()
started = true
}
@@ -149,22 +152,37 @@
@AnyThread
fun stop() = bgExecutor.execute {
started = false
+ controller?.unregisterCallback(this)
localMediaManager.stopScan()
localMediaManager.unregisterCallback(this)
}
fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
- val route = controller?.let {
+ val routingSession = controller?.let {
mr2manager.getRoutingSessionForMediaController(it)
}
+ val selectedRoutes = routingSession?.let {
+ mr2manager.getSelectedRoutes(it)
+ }
with(pw) {
println(" current device is ${current?.name}")
val type = controller?.playbackInfo?.playbackType
- println(" PlaybackType=$type (1 for local, 2 for remote)")
- println(" route=$route")
+ println(" PlaybackType=$type (1 for local, 2 for remote) cached=$playbackType")
+ println(" routingSession=$routingSession")
+ println(" selectedRoutes=$selectedRoutes")
}
}
+ @WorkerThread
+ override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
+ val newPlaybackType = info?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
+ if (newPlaybackType == playbackType) {
+ return
+ }
+ playbackType = newPlaybackType
+ updateCurrent()
+ }
+
override fun onDeviceListUpdate(devices: List<MediaDevice>?) = bgExecutor.execute {
updateCurrent()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 5b59214..5c1c60c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -28,6 +28,7 @@
import android.provider.Settings
import android.service.media.MediaBrowserService
import android.util.Log
+import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -47,7 +48,8 @@
private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
@Background private val backgroundExecutor: Executor,
- private val tunerService: TunerService
+ private val tunerService: TunerService,
+ private val mediaBrowserFactory: ResumeMediaBrowserFactory
) : MediaDataManager.Listener {
private var useMediaResumption: Boolean = Utils.useMediaResumption(context)
@@ -59,7 +61,8 @@
private var mediaBrowser: ResumeMediaBrowser? = null
private var currentUserId: Int = context.userId
- private val userChangeReceiver = object : BroadcastReceiver() {
+ @VisibleForTesting
+ val userChangeReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_USER_UNLOCKED == intent.action) {
loadMediaResumptionControls()
@@ -152,7 +155,7 @@
resumeComponents.forEach {
if (!blockedApps.contains(it.packageName)) {
- val browser = ResumeMediaBrowser(context, mediaBrowserCallback, it)
+ val browser = mediaBrowserFactory.create(mediaBrowserCallback, it)
browser.findRecentMedia()
}
}
@@ -193,14 +196,10 @@
private fun tryUpdateResumptionList(key: String, componentName: ComponentName) {
Log.d(TAG, "Testing if we can connect to $componentName")
mediaBrowser?.disconnect()
- mediaBrowser = ResumeMediaBrowser(context,
+ mediaBrowser = mediaBrowserFactory.create(
object : ResumeMediaBrowser.Callback() {
override fun onConnected() {
- Log.d(TAG, "yes we can resume with $componentName")
- mediaDataManager.setResumeAction(key, getResumeAction(componentName))
- updateResumptionList(componentName)
- mediaBrowser?.disconnect()
- mediaBrowser = null
+ Log.d(TAG, "Connected to $componentName")
}
override fun onError() {
@@ -209,6 +208,19 @@
mediaBrowser?.disconnect()
mediaBrowser = null
}
+
+ override fun addTrack(
+ desc: MediaDescription,
+ component: ComponentName,
+ browser: ResumeMediaBrowser
+ ) {
+ // Since this is a test, just save the component for later
+ Log.d(TAG, "Can get resumable media from $componentName")
+ mediaDataManager.setResumeAction(key, getResumeAction(componentName))
+ updateResumptionList(componentName)
+ mediaBrowser?.disconnect()
+ mediaBrowser = null
+ }
},
componentName)
mediaBrowser?.testConnection()
@@ -245,7 +257,7 @@
private fun getResumeAction(componentName: ComponentName): Runnable {
return Runnable {
mediaBrowser?.disconnect()
- mediaBrowser = ResumeMediaBrowser(context,
+ mediaBrowser = mediaBrowserFactory.create(
object : ResumeMediaBrowser.Callback() {
override fun onConnected() {
if (mediaBrowser?.token == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 6bd5274..51dbfa7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -126,6 +126,7 @@
fun destroy() {
mediaController?.unregisterCallback(this)
+ cancellation?.run()
}
override fun onPlaybackStateChanged(state: PlaybackState?) {
@@ -182,4 +183,4 @@
cancellation = null
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index 68b6785..a4d4436 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -30,6 +30,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.List;
/**
@@ -46,6 +48,7 @@
private static final String TAG = "ResumeMediaBrowser";
private final Context mContext;
private final Callback mCallback;
+ private MediaBrowserFactory mBrowserFactory;
private MediaBrowser mMediaBrowser;
private ComponentName mComponentName;
@@ -55,10 +58,12 @@
* @param callback used to report media items found
* @param componentName Component name of the MediaBrowserService this browser will connect to
*/
- public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName) {
+ public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName,
+ MediaBrowserFactory browserFactory) {
mContext = context;
mCallback = callback;
mComponentName = componentName;
+ mBrowserFactory = browserFactory;
}
/**
@@ -74,7 +79,7 @@
disconnect();
Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
- mMediaBrowser = new MediaBrowser(mContext,
+ mMediaBrowser = mBrowserFactory.create(
mComponentName,
mConnectionCallback,
rootHints);
@@ -88,17 +93,19 @@
List<MediaBrowser.MediaItem> children) {
if (children.size() == 0) {
Log.d(TAG, "No children found for " + mComponentName);
- return;
- }
- // We ask apps to return a playable item as the first child when sending
- // a request with EXTRA_RECENT; if they don't, no resume controls
- MediaBrowser.MediaItem child = children.get(0);
- MediaDescription desc = child.getDescription();
- if (child.isPlayable() && mMediaBrowser != null) {
- mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
- ResumeMediaBrowser.this);
+ mCallback.onError();
} else {
- Log.d(TAG, "Child found but not playable for " + mComponentName);
+ // We ask apps to return a playable item as the first child when sending
+ // a request with EXTRA_RECENT; if they don't, no resume controls
+ MediaBrowser.MediaItem child = children.get(0);
+ MediaDescription desc = child.getDescription();
+ if (child.isPlayable() && mMediaBrowser != null) {
+ mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
+ ResumeMediaBrowser.this);
+ } else {
+ Log.d(TAG, "Child found but not playable for " + mComponentName);
+ mCallback.onError();
+ }
}
disconnect();
}
@@ -131,7 +138,7 @@
Log.d(TAG, "Service connected for " + mComponentName);
if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
String root = mMediaBrowser.getRoot();
- if (!TextUtils.isEmpty(root)) {
+ if (!TextUtils.isEmpty(root) && mMediaBrowser != null) {
mCallback.onConnected();
mMediaBrowser.subscribe(root, mSubscriptionCallback);
return;
@@ -182,7 +189,7 @@
disconnect();
Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
- mMediaBrowser = new MediaBrowser(mContext, mComponentName,
+ mMediaBrowser = mBrowserFactory.create(mComponentName,
new MediaBrowser.ConnectionCallback() {
@Override
public void onConnected() {
@@ -192,7 +199,7 @@
return;
}
MediaSession.Token token = mMediaBrowser.getSessionToken();
- MediaController controller = new MediaController(mContext, token);
+ MediaController controller = createMediaController(token);
controller.getTransportControls();
controller.getTransportControls().prepare();
controller.getTransportControls().play();
@@ -212,6 +219,11 @@
mMediaBrowser.connect();
}
+ @VisibleForTesting
+ protected MediaController createMediaController(MediaSession.Token token) {
+ return new MediaController(mContext, token);
+ }
+
/**
* Get the media session token
* @return the token, or null if the MediaBrowser is null or disconnected
@@ -235,42 +247,19 @@
/**
* Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser.
- * ResumeMediaBrowser.Callback#onError or ResumeMediaBrowser.Callback#onConnected will be called
- * depending on whether it was successful.
+ * If it can connect, ResumeMediaBrowser.Callback#onConnected will be called. If valid media is
+ * found, then ResumeMediaBrowser.Callback#addTrack will also be called. This allows for more
+ * detailed logging if the service has issues. If it cannot connect, or cannot find valid media,
+ * then ResumeMediaBrowser.Callback#onError will be called.
* ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed.
*/
public void testConnection() {
disconnect();
- final MediaBrowser.ConnectionCallback connectionCallback =
- new MediaBrowser.ConnectionCallback() {
- @Override
- public void onConnected() {
- Log.d(TAG, "connected");
- if (mMediaBrowser == null || !mMediaBrowser.isConnected()
- || TextUtils.isEmpty(mMediaBrowser.getRoot())) {
- mCallback.onError();
- } else {
- mCallback.onConnected();
- }
- }
-
- @Override
- public void onConnectionSuspended() {
- Log.d(TAG, "suspended");
- mCallback.onError();
- }
-
- @Override
- public void onConnectionFailed() {
- Log.d(TAG, "failed");
- mCallback.onError();
- }
- };
Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
- mMediaBrowser = new MediaBrowser(mContext,
+ mMediaBrowser = mBrowserFactory.create(
mComponentName,
- connectionCallback,
+ mConnectionCallback,
rootHints);
mMediaBrowser.connect();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
new file mode 100644
index 0000000..2261aa5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media;
+
+import android.content.ComponentName;
+import android.content.Context;
+
+import javax.inject.Inject;
+
+/**
+ * Testable wrapper around {@link ResumeMediaBrowser} constructor
+ */
+public class ResumeMediaBrowserFactory {
+ private final Context mContext;
+ private final MediaBrowserFactory mBrowserFactory;
+
+ @Inject
+ public ResumeMediaBrowserFactory(Context context, MediaBrowserFactory browserFactory) {
+ mContext = context;
+ mBrowserFactory = browserFactory;
+ }
+
+ /**
+ * Creates a new ResumeMediaBrowser.
+ *
+ * @param callback will be called on connection or error, and addTrack when media item found
+ * @param componentName component to browse
+ * @return
+ */
+ public ResumeMediaBrowser create(ResumeMediaBrowser.Callback callback,
+ ComponentName componentName) {
+ return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 9fc64d5..9b6a9ea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -42,7 +42,7 @@
public class MediaOutputAdapter extends MediaOutputBaseAdapter {
private static final String TAG = "MediaOutputAdapter";
- private static final int PAIR_NEW = 1;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public MediaOutputAdapter(MediaOutputController controller) {
super(controller);
@@ -58,11 +58,14 @@
@Override
public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) {
- if (mController.isZeroMode() && position == (mController.getMediaDevices().size())) {
- viewHolder.onBind(PAIR_NEW);
- } else if (position < (mController.getMediaDevices().size())) {
- viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position));
- } else {
+ final int size = mController.getMediaDevices().size();
+ if (mController.isZeroMode() && position == size) {
+ viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */,
+ true /* bottomMargin */);
+ } else if (position < size) {
+ viewHolder.onBind(((List<MediaDevice>) (mController.getMediaDevices())).get(position),
+ position == 0 /* topMargin */, position == (size - 1) /* bottomMargin */);
+ } else if (DEBUG) {
Log.d(TAG, "Incorrect position: " + position);
}
}
@@ -83,7 +86,7 @@
}
void onItemClick(int customizedItem) {
- if (customizedItem == PAIR_NEW) {
+ if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
mController.launchBluetoothPairing();
}
}
@@ -112,51 +115,49 @@
}
@Override
- void onBind(MediaDevice device) {
- super.onBind(device);
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
+ super.onBind(device, topMargin, bottomMargin);
if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
- setTwoLineLayout(device, true);
- mProgressBar.setVisibility(View.VISIBLE);
- mSeekBar.setVisibility(View.GONE);
- mSubTitleText.setVisibility(View.GONE);
+ setTwoLineLayout(device, null /* title */, true /* bFocused */,
+ false /* showSeekBar*/, true /* showProgressBar */,
+ false /* showSubtitle */);
} else {
- setSingleLineLayout(getItemTitle(device), false);
+ setSingleLineLayout(getItemTitle(device), false /* bFocused */);
}
} else {
// Set different layout for each device
if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
- setTwoLineLayout(device, false);
- mSubTitleText.setVisibility(View.VISIBLE);
- mSeekBar.setVisibility(View.GONE);
- mProgressBar.setVisibility(View.GONE);
+ setTwoLineLayout(device, null /* title */, false /* bFocused */,
+ false /* showSeekBar*/, false /* showProgressBar */,
+ true /* showSubtitle */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
mFrameLayout.setOnClickListener(v -> onItemClick(device));
} else if (!mController.hasAdjustVolumeUserRestriction()
&& isCurrentConnected(device)) {
- setTwoLineLayout(device, true);
- mSeekBar.setVisibility(View.VISIBLE);
- mProgressBar.setVisibility(View.GONE);
- mSubTitleText.setVisibility(View.GONE);
+ setTwoLineLayout(device, null /* title */, true /* bFocused */,
+ true /* showSeekBar*/, false /* showProgressBar */,
+ false /* showSubtitle */);
initSeekbar(device);
} else {
- setSingleLineLayout(getItemTitle(device), false);
+ setSingleLineLayout(getItemTitle(device), false /* bFocused */);
mFrameLayout.setOnClickListener(v -> onItemClick(device));
}
}
}
@Override
- void onBind(int customizedItem) {
- if (customizedItem == PAIR_NEW) {
+ void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
+ super.onBind(customizedItem, topMargin, bottomMargin);
+ if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
setSingleLineLayout(mContext.getText(R.string.media_output_dialog_pairing_new),
- false);
+ false /* bFocused */);
final Drawable d = mContext.getDrawable(R.drawable.ic_add);
d.setColorFilter(new PorterDuffColorFilter(
Utils.getColorAccentDefaultColor(mContext), PorterDuff.Mode.SRC_IN));
mTitleIcon.setImageDrawable(d);
- mFrameLayout.setOnClickListener(v -> onItemClick(PAIR_NEW));
+ mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index 7579c25..01dc6c4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -44,9 +44,12 @@
private static final String FONT_SELECTED_TITLE = "sans-serif-medium";
private static final String FONT_TITLE = "sans-serif";
+ static final int CUSTOMIZED_ITEM_PAIR_NEW = 1;
+
final MediaOutputController mController;
private boolean mIsDragging;
+ private int mMargin;
Context mContext;
View mHolderView;
@@ -60,6 +63,8 @@
public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup,
int viewType) {
mContext = viewGroup.getContext();
+ mMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_list_margin);
mHolderView = LayoutInflater.from(mContext).inflate(R.layout.media_output_list_item,
viewGroup, false);
@@ -106,12 +111,26 @@
mSeekBar = view.requireViewById(R.id.volume_seekbar);
}
- void onBind(MediaDevice device) {
+ void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
mTitleIcon.setImageIcon(mController.getDeviceIconCompat(device).toIcon(mContext));
+ setMargin(topMargin, bottomMargin);
}
- void onBind(int customizedItem) { }
+ void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) {
+ setMargin(topMargin, bottomMargin);
+ }
+ private void setMargin(boolean topMargin, boolean bottomMargin) {
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout
+ .getLayoutParams();
+ if (topMargin) {
+ params.topMargin = mMargin;
+ }
+ if (bottomMargin) {
+ params.bottomMargin = mMargin;
+ }
+ mFrameLayout.setLayoutParams(params);
+ }
void setSingleLineLayout(CharSequence title, boolean bFocused) {
mTitleText.setVisibility(View.VISIBLE);
mTwoLineLayout.setVisibility(View.GONE);
@@ -123,10 +142,19 @@
}
}
- void setTwoLineLayout(MediaDevice device, boolean bFocused) {
+ void setTwoLineLayout(MediaDevice device, CharSequence title, boolean bFocused,
+ boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE);
- mTwoLineTitleText.setText(getItemTitle(device));
+ mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
+ mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
+ mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
+ if (device == null) {
+ mTwoLineTitleText.setText(title);
+ } else {
+ mTwoLineTitleText.setText(getItemTitle(device));
+ }
+
if (bFocused) {
mTwoLineTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE,
Typeface.NORMAL));
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index f8f4f4d..ebca8a7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -33,7 +33,6 @@
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.Button;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -69,12 +68,9 @@
private LinearLayout mDeviceListLayout;
private Button mDoneButton;
private Button mStopButton;
- private View mListBottomPadding;
private int mListMaxHeight;
MediaOutputBaseAdapter mAdapter;
- FrameLayout mGroupItemController;
- View mGroupDivider;
private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
// Set max height for list
@@ -114,12 +110,9 @@
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
mHeaderIcon = mDialogView.requireViewById(R.id.header_icon);
mDevicesRecyclerView = mDialogView.requireViewById(R.id.list_result);
- mGroupItemController = mDialogView.requireViewById(R.id.group_item_controller);
- mGroupDivider = mDialogView.requireViewById(R.id.group_item_divider);
mDeviceListLayout = mDialogView.requireViewById(R.id.device_list);
mDoneButton = mDialogView.requireViewById(R.id.done);
mStopButton = mDialogView.requireViewById(R.id.stop);
- mListBottomPadding = mDialogView.requireViewById(R.id.list_bottom_padding);
mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
mDeviceListLayoutListener);
@@ -162,7 +155,9 @@
}
if (mHeaderIcon.getVisibility() == View.VISIBLE) {
final int size = getHeaderIconSize();
- mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size, size));
+ final int padding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_header_icon_padding);
+ mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
}
// Update title and subtitle
mHeaderTitle.setText(getHeaderText());
@@ -178,12 +173,8 @@
if (!mAdapter.isDragging()) {
mAdapter.notifyDataSetChanged();
}
- // Add extra padding when device amount is less than 6
- if (mMediaOutputController.getMediaDevices().size() < 6) {
- mListBottomPadding.setVisibility(View.VISIBLE);
- } else {
- mListBottomPadding.setVisibility(View.GONE);
- }
+ // Show when remote media session is available
+ mStopButton.setVisibility(getStopButtonVisibility());
}
abstract int getHeaderIconRes();
@@ -196,6 +187,8 @@
abstract CharSequence getHeaderSubtitle();
+ abstract int getStopButtonVisibility();
+
@Override
public void onMediaChanged() {
mMainThreadHandler.post(() -> refresh());
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 64d20a27..b1f1bda 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -24,6 +24,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadata;
+import android.media.MediaRoute2Info;
import android.media.RoutingSessionInfo;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
@@ -63,7 +64,7 @@
public class MediaOutputController implements LocalMediaManager.DeviceCallback{
private static final String TAG = "MediaOutputController";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final String mPackageName;
private final Context mContext;
@@ -406,6 +407,14 @@
mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
}
+ boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
+ final List<String> features = device.getFeatures();
+ return (features.contains(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK)
+ || features.contains(MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK)
+ || features.contains(MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK)
+ || features.contains(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK));
+ }
+
private final MediaController.Callback mCb = new MediaController.Callback() {
@Override
public void onMetadataChanged(MediaMetadata metadata) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index ac9d8ce..a892a12 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -45,8 +45,6 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mGroupItemController.setVisibility(View.GONE);
- mGroupDivider.setVisibility(View.GONE);
}
@Override
@@ -74,4 +72,10 @@
CharSequence getHeaderSubtitle() {
return mMediaOutputController.getHeaderSubTitle();
}
+
+ @Override
+ int getStopButtonVisibility() {
+ return mMediaOutputController.isActiveRemoteDevice(
+ mMediaOutputController.getCurrentConnectedMediaDevice()) ? View.VISIBLE : View.GONE;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index bc1dca5..4cdca4c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -33,10 +33,22 @@
private val shadeController: ShadeController,
private val starter: ActivityStarter
) {
+ companion object {
+ var mediaOutputDialog: MediaOutputDialog? = null
+ }
+
/** Creates a [MediaOutputDialog] for the given package. */
fun create(packageName: String, aboveStatusBar: Boolean) {
- MediaOutputController(context, packageName, mediaSessionManager, lbm, shadeController,
- starter).run {
+ mediaOutputDialog?.dismiss()
+
+ mediaOutputDialog = MediaOutputController(context, packageName, mediaSessionManager, lbm,
+ shadeController, starter).run {
MediaOutputDialog(context, aboveStatusBar, this) }
}
+
+ /** dismiss [MediaOutputDialog] if exist. */
+ fun dismiss() {
+ mediaOutputDialog?.dismiss()
+ mediaOutputDialog = null
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/Pip.java b/packages/SystemUI/src/com/android/systemui/pip/Pip.java
index b068370..2b11550 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/Pip.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/Pip.java
@@ -16,24 +16,21 @@
package com.android.systemui.pip;
-import android.content.res.Configuration;
+import android.app.ActivityManager;
+import android.content.ComponentName;
import android.media.session.MediaController;
+import com.android.systemui.pip.phone.PipTouchHandler;
import com.android.systemui.pip.tv.PipController;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* Interface to engage picture in picture feature.
*/
public interface Pip {
/**
- * Called when showing Pip menu.
- */
- void showPictureInPictureMenu();
-
- /**
* Registers {@link com.android.systemui.pip.tv.PipController.Listener} that gets called.
* whenever receiving notification on changes in PIP.
*/
@@ -53,6 +50,14 @@
}
/**
+ * Dump the current state and information if need.
+ *
+ * @param pw The stream to dump information to.
+ */
+ default void dump(PrintWriter pw) {
+ }
+
+ /**
* Expand PIP, it's possible that specific request to activate the window via Alt-tab.
*/
default void expandPip() {
@@ -64,7 +69,11 @@
* @return The state of defined in PipController.
*/
default int getPlaybackState() {
- return 0;
+ return -1;
+ }
+
+ default PipTouchHandler getPipTouchHandler() {
+ return null;
}
/**
@@ -95,9 +104,61 @@
}
/**
- * Called when configuration change invoked.
+ * Called whenever an Activity is moved to the pinned stack from another stack.
*/
- void onConfigurationChanged(Configuration newConfig);
+ default void onActivityPinned(String packageName) {
+ }
+
+ /**
+ * Called whenever an Activity is moved from the pinned stack to another stack
+ */
+ default void onActivityUnpinned(ComponentName topActivity) {
+ }
+
+ /**
+ * Called whenever IActivityManager.startActivity is called on an activity that is already
+ * running, but the task is either brought to the front or a new Intent is delivered to it.
+ *
+ * @param task information about the task the activity was relaunched into
+ * @param clearedTask whether or not the launch activity also cleared the task as a part of
+ * starting
+ */
+ default void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean clearedTask) {
+ }
+
+ /**
+ * Called when display size or font size of settings changed
+ */
+ default void onDensityOrFontScaleChanged() {
+ }
+
+ /**
+ * Called when overlay package change invoked.
+ */
+ default void onOverlayChanged() {
+ }
+
+ /**
+ * Registers the session listener for the current user.
+ */
+ default void registerSessionListenerForCurrentUser() {
+ }
+
+ /**
+ * Called when SysUI state changed.
+ *
+ * @param isSysUiStateValid Is SysUI state valid or not.
+ * @param flag Current SysUI state.
+ */
+ default void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
+ }
+
+ /**
+ * Called when task stack changed.
+ */
+ default void onTaskStackChanged() {
+ }
/**
* Removes a {@link PipController.Listener} from PipController.
@@ -137,6 +198,14 @@
}
/**
+ * Registers the pinned stack animation listener.
+ *
+ * @param callback The callback of pinned stack animation.
+ */
+ default void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
+ }
+
+ /**
* Set the pinned stack with {@link PipAnimationController.AnimationType}
*
* @param animationType The pre-defined {@link PipAnimationController.AnimationType}
@@ -145,12 +214,9 @@
}
/**
- * Registers the pinned stack animation listener.
- *
- * @param listener The listener of pinned stack animation.
+ * Called when showing Pip menu.
*/
- default void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
- }
+ void showPictureInPictureMenu();
/**
* Suspends resizing operation on the Pip until {@link #resumePipResizing} is called.
@@ -159,12 +225,4 @@
*/
default void suspendPipResizing(int reason) {
}
-
- /**
- * Dump the current state and information if need.
- *
- * @param pw The stream to dump information to.
- */
- default void dump(PrintWriter pw) {
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
index 3e98169..fc724cb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipSurfaceTransactionHelper.java
@@ -24,16 +24,14 @@
import android.view.SurfaceControl;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.wm.shell.R;
/**
* Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition.
*/
@SysUISingleton
-public class PipSurfaceTransactionHelper implements ConfigurationController.ConfigurationListener {
+public class PipSurfaceTransactionHelper {
- private final Context mContext;
private final boolean mEnableCornerRadius;
private int mCornerRadius;
@@ -44,17 +42,21 @@
private final RectF mTmpDestinationRectF = new RectF();
private final Rect mTmpDestinationRect = new Rect();
- public PipSurfaceTransactionHelper(Context context, ConfigurationController configController) {
+ public PipSurfaceTransactionHelper(Context context) {
final Resources res = context.getResources();
- mContext = context;
mEnableCornerRadius = res.getBoolean(R.bool.config_pipEnableRoundCorner);
- configController.addCallback(this);
}
- @Override
- public void onDensityOrFontScaleChanged() {
- final Resources res = mContext.getResources();
- mCornerRadius = res.getDimensionPixelSize(R.dimen.pip_corner_radius);
+ /**
+ * Called when display size or font size of settings changed
+ *
+ * @param context the current context
+ */
+ public void onDensityOrFontScaleChanged(Context context) {
+ if (mEnableCornerRadius) {
+ final Resources res = context.getResources();
+ mCornerRadius = res.getDimensionPixelSize(R.dimen.pip_corner_radius);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 6cc0cd8..fb86535 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -130,7 +130,6 @@
}
}
- private final Context mContext;
private final Handler mMainHandler;
private final Handler mUpdateHandler;
private final PipBoundsHandler mPipBoundsHandler;
@@ -249,7 +248,6 @@
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@NonNull ShellTaskOrganizer shellTaskOrganizer) {
- mContext = context;
mMainHandler = new Handler(Looper.getMainLooper());
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
mPipBoundsHandler = boundsHandler;
@@ -550,23 +548,24 @@
/**
* Setup the ViewHost and attach the provided menu view to the ViewHost.
+ * @return The input token belonging to the PipMenuView.
*/
- public void attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) {
+ public IBinder attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) {
if (mPipMenuSurface != null) {
Log.e(TAG, "PIP Menu View already created and attached.");
- return;
+ return null;
}
if (mLeash == null) {
Log.e(TAG, "PiP Leash is not yet ready.");
- return;
+ return null;
}
if (Looper.getMainLooper() != Looper.myLooper()) {
throw new RuntimeException("PipMenuView needs to be attached on the main thread.");
}
-
- mPipViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(),
+ final Context context = menuView.getContext();
+ mPipViewHost = new SurfaceControlViewHost(context, context.getDisplay(),
(android.os.Binder) null);
mPipMenuSurface = mPipViewHost.getSurfacePackage().getSurfaceControl();
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
@@ -575,6 +574,8 @@
transaction.setRelativeLayer(mPipMenuSurface, mLeash, 1);
transaction.apply();
mPipViewHost.setView(menuView, lp);
+
+ return mPipViewHost.getSurfacePackage().getInputToken();
}
@@ -659,6 +660,13 @@
}
/**
+ * Called when display size or font size of settings changed
+ */
+ public void onDensityOrFontScaleChanged(Context context) {
+ mSurfaceTransactionHelper.onDensityOrFontScaleChanged(context);
+ }
+
+ /**
* TODO(b/152809058): consolidate the display info handling logic in SysUI
*
* @param destinationBoundsOut the current destination bounds will be populated to this param
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
index 8a2e9e2..6253043 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipController.java
@@ -16,55 +16,37 @@
package com.android.systemui.pip.phone;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static com.android.systemui.pip.PipAnimationController.isOutPipDirection;
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.ActivityTaskManager.RootTaskInfo;
-import android.app.IActivityManager;
import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ParceledListSlice;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import android.util.Pair;
import android.view.DisplayInfo;
import android.view.IPinnedStackController;
import android.window.WindowContainerTransaction;
-import com.android.systemui.Dependency;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipBoundsHandler;
-import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
-import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* Manages the picture-in-picture (PIP) UI and states for Phones.
@@ -74,7 +56,6 @@
private static final String TAG = "PipController";
private Context mContext;
- private IActivityManager mActivityManager;
private Handler mHandler = new Handler();
private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
@@ -83,13 +64,13 @@
protected final Rect mReentryBounds = new Rect();
private DisplayController mDisplayController;
- private InputConsumerController mInputConsumerController;
private PipAppOpsListener mAppOpsListener;
private PipBoundsHandler mPipBoundsHandler;
private PipMediaController mMediaController;
private PipTouchHandler mTouchHandler;
- private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
- private IPinnedStackAnimationListener mPinnedStackAnimationRecentsListener;
+ private Consumer<Boolean> mPinnedStackAnimationRecentsCallback;
+ private WindowManagerShellWrapper mWindowManagerShellWrapper;
+
private boolean mIsInFixedRotation;
protected PipMenuActivityController mMenuController;
@@ -156,50 +137,10 @@
};
/**
- * Handler for system task stack changes.
- */
- private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- @Override
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
- mTouchHandler.onActivityPinned();
- mMediaController.onActivityPinned();
- mMenuController.onActivityPinned();
- mAppOpsListener.onActivityPinned(packageName);
-
- Dependency.get(UiOffloadThread.class).execute(() -> {
- WindowManagerWrapper.getInstance().setPipVisibility(true);
- });
- }
-
- @Override
- public void onActivityUnpinned() {
- final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPipActivity(
- mContext, mActivityManager);
- final ComponentName topActivity = topPipActivityInfo.first;
- mMenuController.onActivityUnpinned();
- mTouchHandler.onActivityUnpinned(topActivity);
- mAppOpsListener.onActivityUnpinned();
-
- Dependency.get(UiOffloadThread.class).execute(() -> {
- WindowManagerWrapper.getInstance().setPipVisibility(topActivity != null);
- });
- }
-
- @Override
- public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
- boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
- if (task.configuration.windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_PINNED) {
- return;
- }
- mTouchHandler.getMotionHelper().expandLeavePip(clearedTask /* skipAnimation */);
- }
- };
-
- /**
* Handler for messages from the PIP controller.
*/
- private class PipControllerPinnedStackListener extends PinnedStackListener {
+ private class PipControllerPinnedStackListener extends
+ PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onListenerRegistered(IPinnedStackController controller) {
mHandler.post(() -> mTouchHandler.setPinnedStackController(controller));
@@ -237,7 +178,10 @@
@Override
public void onConfigurationChanged() {
- mHandler.post(() -> mPipBoundsHandler.onConfigurationChanged(mContext));
+ mHandler.post(() -> {
+ mPipBoundsHandler.onConfigurationChanged(mContext);
+ mTouchHandler.onConfigurationChanged();
+ });
}
@Override
@@ -249,52 +193,36 @@
}
}
- public ConfigurationController.ConfigurationListener mOverlayChangedListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onOverlayChanged() {
- mHandler.post(() -> {
- mPipBoundsHandler.onOverlayChanged(mContext, mContext.getDisplay());
- updateMovementBounds(null /* toBounds */,
- false /* fromRotation */, false /* fromImeAdjustment */,
- false /* fromShelfAdjustment */,
- null /* windowContainerTransaction */);
- });
- }
- };
-
- public PipController(Context context, BroadcastDispatcher broadcastDispatcher,
- ConfigurationController configController,
- DeviceConfigProxy deviceConfig,
+ public PipController(Context context,
DisplayController displayController,
- FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState,
+ PipAppOpsListener pipAppOpsListener,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ PipMediaController pipMediaController,
+ PipMenuActivityController pipMenuActivityController,
PipTaskOrganizer pipTaskOrganizer,
- PipUiEventLogger pipUiEventLogger) {
+ PipTouchHandler pipTouchHandler,
+ WindowManagerShellWrapper windowManagerShellWrapper
+ ) {
mContext = context;
- mActivityManager = ActivityManager.getService();
if (PipUtils.hasSystemFeature(mContext)) {
- initController(context, broadcastDispatcher, configController, deviceConfig,
- displayController, floatingContentCoordinator, sysUiState, pipBoundsHandler,
- pipSurfaceTransactionHelper, pipTaskOrganizer, pipUiEventLogger);
+ initController(context, displayController, pipAppOpsListener, pipBoundsHandler,
+ pipMediaController, pipMenuActivityController, pipTaskOrganizer,
+ pipTouchHandler, windowManagerShellWrapper);
} else {
Log.w(TAG, "Device not support PIP feature");
}
}
- private void initController(Context context, BroadcastDispatcher broadcastDispatcher,
- ConfigurationController configController,
- DeviceConfigProxy deviceConfig,
+ private void initController(Context context,
DisplayController displayController,
- FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState,
+ PipAppOpsListener pipAppOpsListener,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
+ PipMediaController pipMediaController,
+ PipMenuActivityController pipMenuActivityController,
PipTaskOrganizer pipTaskOrganizer,
- PipUiEventLogger pipUiEventLogger) {
+ PipTouchHandler pipTouchHandler,
+ WindowManagerShellWrapper windowManagerShellWrapper) {
// Ensure that we are the primary user's SystemUI.
final int processUser = UserManager.get(context).getUserHandle();
@@ -302,28 +230,15 @@
throw new IllegalStateException("Non-primary Pip component not currently supported.");
}
- try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(
- new PipControllerPinnedStackListener());
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register pinned stack listener", e);
- }
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-
+ mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
mPipBoundsHandler = pipBoundsHandler;
- mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTaskOrganizer.registerPipTransitionCallback(this);
- mInputConsumerController = InputConsumerController.getPipInputConsumer();
- mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
- mMenuController = new PipMenuActivityController(context,
- mMediaController, mInputConsumerController, mPipTaskOrganizer);
- mTouchHandler = new PipTouchHandler(context, mActivityManager,
- mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
- floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger);
- mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
- mTouchHandler.getMotionHelper());
+ mMediaController = pipMediaController;
+ mMenuController = pipMenuActivityController;
+ mTouchHandler = pipTouchHandler;
+ mAppOpsListener = pipAppOpsListener;
displayController.addDisplayChangingController(mRotationController);
displayController.addDisplayWindowListener(mFixedRotationListener);
@@ -333,26 +248,69 @@
context.getDisplay().getDisplayInfo(displayInfo);
mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
- configController.addCallback(mOverlayChangedListener);
-
try {
- RootTaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo(
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
- if (taskInfo != null) {
- // If SystemUI restart, and it already existed a pinned stack,
- // register the pip input consumer to ensure touch can send to it.
- mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
- }
- } catch (RemoteException | UnsupportedOperationException e) {
- e.printStackTrace();
+ mWindowManagerShellWrapper.addPinnedStackListener(
+ new PipControllerPinnedStackListener());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register pinned stack listener", e);
}
}
- /**
- * Updates the PIP per configuration changed.
- */
- public void onConfigurationChanged(Configuration newConfig) {
- mTouchHandler.onConfigurationChanged();
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ mHandler.post(() -> {
+ mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext);
+ });
+ }
+
+ @Override
+ public void onActivityPinned(String packageName) {
+ mHandler.post(() -> {
+ mTouchHandler.onActivityPinned();
+ mMediaController.onActivityPinned();
+ mMenuController.onActivityPinned();
+ mAppOpsListener.onActivityPinned(packageName);
+ });
+ }
+
+ @Override
+ public void onActivityUnpinned(ComponentName topActivity) {
+ mHandler.post(() -> {
+ mMenuController.onActivityUnpinned();
+ mTouchHandler.onActivityUnpinned(topActivity);
+ mAppOpsListener.onActivityUnpinned();
+ });
+ }
+
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean clearedTask) {
+ if (task.configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_PINNED) {
+ return;
+ }
+ mTouchHandler.getMotionHelper().expandLeavePip(clearedTask /* skipAnimation */);
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ mHandler.post(() -> {
+ mPipBoundsHandler.onOverlayChanged(mContext, mContext.getDisplay());
+ updateMovementBounds(null /* toBounds */,
+ false /* fromRotation */, false /* fromImeAdjustment */,
+ false /* fromShelfAdjustment */,
+ null /* windowContainerTransaction */);
+ });
+ }
+
+ @Override
+ public void registerSessionListenerForCurrentUser() {
+ mMediaController.registerSessionListenerForCurrentUser();
+ }
+
+ @Override
+ public void onSystemUiStateChanged(boolean isValidState, int flag) {
+ mTouchHandler.onSystemUiStateChanged(isValidState);
}
/**
@@ -363,6 +321,11 @@
mTouchHandler.getMotionHelper().expandLeavePip(false /* skipAnimation */);
}
+ @Override
+ public PipTouchHandler getPipTouchHandler() {
+ return mTouchHandler;
+ }
+
/**
* Hides the PIP menu.
*/
@@ -408,8 +371,8 @@
}
@Override
- public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
- mHandler.post(() -> mPinnedStackAnimationRecentsListener = listener);
+ public void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
+ mHandler.post(() -> mPinnedStackAnimationRecentsCallback = callback);
}
@Override
@@ -421,12 +384,8 @@
}
// Disable touches while the animation is running
mTouchHandler.setTouchEnabled(false);
- if (mPinnedStackAnimationRecentsListener != null) {
- try {
- mPinnedStackAnimationRecentsListener.onPinnedStackAnimationStarted();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to callback recents", e);
- }
+ if (mPinnedStackAnimationRecentsCallback != null) {
+ mPinnedStackAnimationRecentsCallback.accept(true);
}
}
@@ -477,7 +436,6 @@
public void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
- mInputConsumerController.dump(pw, innerPrefix);
mMenuController.dump(pw, innerPrefix);
mTouchHandler.dump(pw, innerPrefix);
mPipBoundsHandler.dump(pw, innerPrefix);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index 361aafa..a5b5092 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -26,19 +26,14 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
-import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
import android.media.session.PlaybackState;
import android.os.UserHandle;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.statusbar.policy.UserInfoController;
import java.util.ArrayList;
import java.util.Collections;
@@ -101,17 +96,11 @@
};
private final MediaSessionManager.OnActiveSessionsChangedListener mSessionsChangedListener =
- new OnActiveSessionsChangedListener() {
- @Override
- public void onActiveSessionsChanged(List<MediaController> controllers) {
- resolveActiveMediaController(controllers);
- }
- };
+ controllers -> resolveActiveMediaController(controllers);
private ArrayList<ActionListener> mListeners = new ArrayList<>();
- public PipMediaController(Context context, IActivityManager activityManager,
- BroadcastDispatcher broadcastDispatcher) {
+ public PipMediaController(Context context, IActivityManager activityManager) {
mContext = context;
mActivityManager = activityManager;
IntentFilter mediaControlFilter = new IntentFilter();
@@ -119,16 +108,12 @@
mediaControlFilter.addAction(ACTION_PAUSE);
mediaControlFilter.addAction(ACTION_NEXT);
mediaControlFilter.addAction(ACTION_PREV);
- broadcastDispatcher.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter);
+ mContext.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter,
+ UserHandle.USER_ALL);
createMediaActions();
mMediaSessionManager =
(MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
-
- // The media session listener needs to be re-registered when switching users
- UserInfoController userInfoController = Dependency.get(UserInfoController.class);
- userInfoController.addCallback((String name, Drawable picture, String userAccount) ->
- registerSessionListenerForCurrentUser());
}
/**
@@ -220,7 +205,7 @@
/**
* Re-registers the session listener for the current user.
*/
- private void registerSessionListenerForCurrentUser() {
+ public void registerSessionListenerForCurrentUser() {
mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener);
mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null,
UserHandle.USER_CURRENT, null);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index 5b07db6..6c23225 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -27,14 +27,15 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Debug;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.MotionEvent;
import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.phone.PipMediaController.ActionListener;
-import com.android.systemui.shared.system.InputConsumerController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -86,7 +87,6 @@
private Context mContext;
private PipTaskOrganizer mPipTaskOrganizer;
private PipMediaController mMediaController;
- private InputConsumerController mInputConsumerController;
private ArrayList<Listener> mListeners = new ArrayList<>();
private ParceledListSlice<RemoteAction> mAppActions;
@@ -94,6 +94,7 @@
private int mMenuState;
private PipMenuView mPipMenuView;
+ private IBinder mPipMenuInputToken;
private ActionListener mMediaActionListener = new ActionListener() {
@Override
@@ -104,12 +105,9 @@
};
public PipMenuActivityController(Context context,
- PipMediaController mediaController, InputConsumerController inputConsumerController,
- PipTaskOrganizer pipTaskOrganizer
- ) {
+ PipMediaController mediaController, PipTaskOrganizer pipTaskOrganizer) {
mContext = context;
mMediaController = mediaController;
- mInputConsumerController = inputConsumerController;
mPipTaskOrganizer = pipTaskOrganizer;
}
@@ -119,14 +117,13 @@
public void onActivityPinned() {
attachPipMenuView();
- mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
}
public void onActivityUnpinned() {
hideMenu();
- mInputConsumerController.unregisterInputConsumer();
mPipTaskOrganizer.detachPipMenuViewHost();
mPipMenuView = null;
+ mPipMenuInputToken = null;
}
public void onPinnedStackAnimationEnded() {
@@ -140,7 +137,13 @@
mPipMenuView = new PipMenuView(mContext, this);
}
- mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, getPipMenuLayoutParams(0, 0));
+
+ // If we haven't gotten the input toekn, that means we haven't had a success attempt
+ // yet at attaching the PipMenuView
+ if (mPipMenuInputToken == null) {
+ mPipMenuInputToken = mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView,
+ getPipMenuLayoutParams(0, 0));
+ }
}
/**
@@ -254,7 +257,9 @@
if (isMenuVisible()) {
// If the menu is visible in either the closed or full state, then hide the menu and
// trigger the animation trigger afterwards
- onStartCallback.run();
+ if (onStartCallback != null) {
+ onStartCallback.run();
+ }
mPipMenuView.hideMenu(onEndCallback);
}
}
@@ -357,6 +362,13 @@
// the menu actions to be updated again.
mMediaController.removeListener(mMediaActionListener);
}
+
+ try {
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ mPipMenuInputToken, menuState != MENU_STATE_NONE /* grantFocus */);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to update focus as menu appears/disappears", e);
+ }
}
mMenuState = menuState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
index c66f442..48ddbff 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
@@ -154,10 +154,6 @@
expandPip();
}
});
- // TODO (b/161710689): Remove this once focusability for Windowless window is working
- findViewById(R.id.expand_button).setFocusable(false);
- mDismissButton.setFocusable(false);
- mSettingsButton.setFocusable(false);
mResizeHandle = findViewById(R.id.resize_handle);
mResizeHandle.setAlpha(0);
@@ -316,7 +312,7 @@
}
void hideMenu(Runnable animationEndCallback) {
- hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false);
+ hideMenu(animationEndCallback, true /* notifyMenuVisibility */, true /* animate */);
}
private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
@@ -394,8 +390,10 @@
// TODO: Check if the action drawable has changed before we reload it
action.getIcon().loadDrawableAsync(mContext, d -> {
- d.setTint(Color.WHITE);
- actionView.setImageDrawable(d);
+ if (d != null) {
+ d.setTint(Color.WHITE);
+ actionView.setImageDrawable(d);
+ }
}, mHandler);
actionView.setContentDescription(action.getContentDescription());
if (action.isEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 08d9b2a..1e3d871 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -21,13 +21,6 @@
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.content.Context;
import android.content.res.Resources;
@@ -50,11 +43,9 @@
import android.view.ViewConfiguration;
import com.android.internal.policy.TaskResizingAlgorithm;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.wm.shell.R;
import java.io.PrintWriter;
@@ -71,21 +62,11 @@
private static final float PINCH_THRESHOLD = 0.05f;
private static final float STARTING_SCALE_FACTOR = 1.0f;
- private static final int INVALID_SYSUI_STATE_MASK =
- SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
- | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
- | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
- | SYSUI_STATE_BOUNCER_SHOWING
- | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
- | SYSUI_STATE_BUBBLES_EXPANDED
- | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
-
private final Context mContext;
private final PipBoundsHandler mPipBoundsHandler;
private final PipMotionHelper mMotionHelper;
private final int mDisplayId;
private final Executor mMainExecutor;
- private final SysUiState mSysUiState;
private final ScaleGestureDetector mScaleGestureDetector;
private final Region mTmpRegion = new Region();
@@ -110,6 +91,7 @@
private boolean mIsAttached;
private boolean mIsEnabled;
private boolean mEnablePinchResize;
+ private boolean mIsSysUiStateValid;
private boolean mThresholdCrossed;
private boolean mUsingPinchToZoom = false;
private float mScaleFactor = STARTING_SCALE_FACTOR;
@@ -123,9 +105,8 @@
private int mCtrlType;
public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
- PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
- PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
- Runnable updateMovementBoundsRunnable, SysUiState sysUiState,
+ PipMotionHelper motionHelper, PipTaskOrganizer pipTaskOrganizer,
+ Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable,
PipUiEventLogger pipUiEventLogger, PipMenuActivityController menuActivityController) {
mContext = context;
mDisplayId = context.getDisplayId();
@@ -135,7 +116,6 @@
mPipTaskOrganizer = pipTaskOrganizer;
mMovementBoundsSupplier = movementBoundsSupplier;
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
- mSysUiState = sysUiState;
mPipMenuActivityController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
@@ -202,7 +182,7 @@
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_PINCH_RESIZE,
/* defaultValue = */ false);
- deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
@@ -218,6 +198,15 @@
reloadResources();
}
+ /**
+ * Called when SysUI state changed.
+ *
+ * @param isSysUiStateValid Is SysUI valid or not.
+ */
+ public void onSystemUiStateChanged(boolean isSysUiStateValid) {
+ mIsSysUiStateValid = isSysUiStateValid;
+ }
+
private void reloadResources() {
final Resources res = mContext.getResources();
mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
@@ -319,6 +308,10 @@
return mTmpRegion.contains(x, y);
}
+ public boolean isUsingPinchToZoom() {
+ return mEnablePinchResize;
+ }
+
public boolean willStartResizeGesture(MotionEvent ev) {
if (isInValidSysUiState()) {
switch (ev.getActionMasked()) {
@@ -401,7 +394,7 @@
}
private boolean isInValidSysUiState() {
- return (mSysUiState.getFlags() & INVALID_SYSUI_STATE_MASK) == 0;
+ return mIsSysUiStateValid;
}
private void onDragCornerResize(MotionEvent ev) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 858683c..1f9125da 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -23,7 +23,6 @@
import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
import android.annotation.SuppressLint;
-import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Resources;
@@ -57,13 +56,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipAnimationController;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.DismissCircleView;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.animation.PhysicsAnimator;
@@ -92,7 +88,6 @@
private final boolean mEnableResize;
private final Context mContext;
private final WindowManager mWindowManager;
- private final IActivityManager mActivityManager;
private final PipBoundsHandler mPipBoundsHandler;
private final PipUiEventLogger mPipUiEventLogger;
@@ -216,18 +211,14 @@
}
@SuppressLint("InflateParams")
- public PipTouchHandler(Context context, IActivityManager activityManager,
+ public PipTouchHandler(Context context,
PipMenuActivityController menuController,
- InputConsumerController inputConsumerController,
PipBoundsHandler pipBoundsHandler,
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
- DeviceConfigProxy deviceConfig,
- SysUiState sysUiState,
PipUiEventLogger pipUiEventLogger) {
// Initialize the Pip input consumer
mContext = context;
- mActivityManager = activityManager;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsHandler = pipBoundsHandler;
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
@@ -238,22 +229,18 @@
mPipBoundsHandler.getSnapAlgorithm(), floatingContentCoordinator);
mPipResizeGestureHandler =
new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
- deviceConfig, pipTaskOrganizer, this::getMovementBounds,
- this::updateMovementBounds, sysUiState, pipUiEventLogger, menuController);
+ pipTaskOrganizer, this::getMovementBounds,
+ this::updateMovementBounds, pipUiEventLogger, menuController);
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
() -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
- menuController::hideMenu);
+ menuController::hideMenu);
Resources res = context.getResources();
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
reloadResources();
- // Register the listener for input consumer touch events
- inputConsumerController.setInputListener(this::handleTouchEvent);
- inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
-
mFloatingContentCoordinator = floatingContentCoordinator;
mConnection = new PipAccessibilityInteractionConnection(mContext, mMotionHelper,
pipTaskOrganizer, mPipBoundsHandler.getSnapAlgorithm(),
@@ -320,15 +307,12 @@
DeviceConfig.NAMESPACE_SYSTEMUI,
PIP_STASHING,
/* defaultValue = */ false);
- deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
context.getMainExecutor(),
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getKeyset().contains(PIP_STASHING)) {
- mEnableStash = properties.getBoolean(
- PIP_STASHING, /* defaultValue = */ false);
- }
+ properties -> {
+ if (properties.getKeyset().contains(PIP_STASHING)) {
+ mEnableStash = properties.getBoolean(
+ PIP_STASHING, /* defaultValue = */ false);
}
});
}
@@ -438,6 +422,15 @@
mShelfHeight = shelfHeight;
}
+ /**
+ * Called when SysUI state changed.
+ *
+ * @param isSysUiStateValid Is SysUI valid or not.
+ */
+ public void onSystemUiStateChanged(boolean isSysUiStateValid) {
+ mPipResizeGestureHandler.onSystemUiStateChanged(isSysUiStateValid);
+ }
+
public void adjustBoundsForRotation(Rect outBounds, Rect curBounds, Rect insetBounds) {
final Rect toMovementBounds = new Rect();
mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(outBounds, insetBounds,
@@ -648,7 +641,10 @@
}
}
- private void onRegistrationChanged(boolean isRegistered) {
+ /**
+ * TODO Add appropriate description
+ */
+ public void onRegistrationChanged(boolean isRegistered) {
mAccessibilityManager.setPictureInPictureActionReplacingConnection(isRegistered
? mConnection : null);
if (!isRegistered && mTouchState.isUserInteracting()) {
@@ -664,7 +660,10 @@
shouldShowResizeHandle());
}
- private boolean handleTouchEvent(InputEvent inputEvent) {
+ /**
+ * TODO Add appropriate description
+ */
+ public boolean handleTouchEvent(InputEvent inputEvent) {
// Skip any non motion events
if (!(inputEvent instanceof MotionEvent)) {
return true;
@@ -832,9 +831,7 @@
// we store back to this snap fraction. Otherwise, we'll reset the snap
// fraction and snap to the closest edge.
if (resize) {
- Rect expandedBounds = new Rect(mExpandedBounds);
- mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
- mMovementBounds, mExpandedMovementBounds, callback);
+ animateToExpandedState(callback);
}
} else if (menuState == MENU_STATE_NONE && mMenuState == MENU_STATE_FULL) {
// Try and restore the PiP to the closest edge, using the saved snap fraction
@@ -860,13 +857,7 @@
}
if (mDeferResizeToNormalBoundsUntilRotation == -1) {
- Rect restoreBounds = new Rect(getUserResizeBounds());
- Rect restoredMovementBounds = new Rect();
- mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(restoreBounds,
- mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
- mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
- restoredMovementBounds, mMovementBounds, false /* immediate */);
- mSavedSnapFraction = -1f;
+ animateToUnexpandedState(getUserResizeBounds());
}
} else {
mSavedSnapFraction = -1f;
@@ -884,6 +875,21 @@
}
}
+ private void animateToExpandedState(Runnable callback) {
+ Rect expandedBounds = new Rect(mExpandedBounds);
+ mSavedSnapFraction = mMotionHelper.animateToExpandedState(expandedBounds,
+ mMovementBounds, mExpandedMovementBounds, callback);
+ }
+
+ private void animateToUnexpandedState(Rect restoreBounds) {
+ Rect restoredMovementBounds = new Rect();
+ mPipBoundsHandler.getSnapAlgorithm().getMovementBounds(restoreBounds,
+ mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0);
+ mMotionHelper.animateToUnexpandedState(restoreBounds, mSavedSnapFraction,
+ restoredMovementBounds, mMovementBounds, false /* immediate */);
+ mSavedSnapFraction = -1f;
+ }
+
/**
* @return the motion helper.
*/
@@ -1026,10 +1032,24 @@
this::flingEndAction /* endAction */);
}
} else if (mTouchState.isDoubleTap()) {
- // Expand to fullscreen if this is a double tap
- // the PiP should be frozen until the transition ends
- setTouchEnabled(false);
- mMotionHelper.expandLeavePip();
+ // If using pinch to zoom, double-tap functions as resizing between max/min size
+ if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
+ final boolean toExpand =
+ mMotionHelper.getBounds().width() < mExpandedBounds.width()
+ && mMotionHelper.getBounds().height() < mExpandedBounds.height();
+ mPipResizeGestureHandler.setUserResizeBounds(toExpand ? mExpandedBounds
+ : mNormalBounds);
+ if (toExpand) {
+ animateToExpandedState(null);
+ } else {
+ animateToUnexpandedState(mNormalBounds);
+ }
+ } else {
+ // Expand to fullscreen if this is a double tap
+ // the PiP should be frozen until the transition ends
+ setTouchEnabled(false);
+ mMotionHelper.expandLeavePip();
+ }
} else if (mMenuState != MENU_STATE_FULL) {
if (!mTouchState.isWaitingForDoubleTap()) {
// User has stalled long enough for this not to be a drag or a double tap, just
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
index 12a545a..6ac4e4cb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
@@ -20,7 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.IActivityTaskManager;
@@ -45,23 +45,16 @@
import android.util.Log;
import android.view.DisplayInfo;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.UiOffloadThread;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipBoundsHandler;
-import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.shared.system.PinnedStackListenerForwarder;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import java.util.ArrayList;
import java.util.List;
-import java.util.Optional;
/**
* Manages the picture-in-picture (PIP) UI and states.
@@ -113,7 +106,6 @@
private Context mContext;
private PipBoundsHandler mPipBoundsHandler;
private PipTaskOrganizer mPipTaskOrganizer;
- private PipSurfaceTransactionHelper mPipSurfaceTransactionHelper;
private IActivityTaskManager mActivityTaskManager;
private MediaSessionManager mMediaSessionManager;
private int mState = STATE_NO_PIP;
@@ -133,6 +125,7 @@
private String[] mLastPackagesResourceGranted;
private PipNotification mPipNotification;
private ParceledListSlice<RemoteAction> mCustomActions;
+ private WindowManagerShellWrapper mWindowManagerShellWrapper;
private int mResizeAnimationDuration;
// Used to calculate the movement bounds
@@ -143,21 +136,9 @@
private boolean mImeVisible;
private int mImeHeightAdjustment;
- private final PinnedStackListener mPinnedStackListener = new PipControllerPinnedStackListener();
-
- private final Runnable mResizePinnedStackRunnable = new Runnable() {
- @Override
- public void run() {
- resizePinnedStack(mResumeResizePinnedStackRunnableState);
- }
- };
- private final Runnable mClosePipRunnable = new Runnable() {
- @Override
- public void run() {
- closePip();
- }
- };
-
+ private final Runnable mResizePinnedStackRunnable =
+ () -> resizePinnedStack(mResumeResizePinnedStackRunnableState);
+ private final Runnable mClosePipRunnable = () -> closePip();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -175,17 +156,23 @@
}
};
private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
- new MediaSessionManager.OnActiveSessionsChangedListener() {
- @Override
- public void onActiveSessionsChanged(List<MediaController> controllers) {
- updateMediaController(controllers);
- }
- };
+ controllers -> updateMediaController(controllers);
+ private final PinnedStackListenerForwarder.PinnedStackListener mPinnedStackListener =
+ new PipControllerPinnedStackListener();
+
+ @Override
+ public void registerSessionListenerForCurrentUser() {
+ // TODO Need confirm if TV have to re-registers when switch user
+ mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
+ mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveMediaSessionListener, null,
+ UserHandle.USER_CURRENT, null);
+ }
/**
* Handler for messages from the PIP controller.
*/
- private class PipControllerPinnedStackListener extends PinnedStackListener {
+ private class PipControllerPinnedStackListener extends
+ PinnedStackListenerForwarder.PinnedStackListener {
@Override
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
mHandler.post(() -> {
@@ -227,18 +214,18 @@
}
}
- public PipController(Context context, BroadcastDispatcher broadcastDispatcher,
+ public PipController(Context context,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- PipTaskOrganizer pipTaskOrganizer) {
+ PipTaskOrganizer pipTaskOrganizer,
+ WindowManagerShellWrapper windowManagerShellWrapper
+ ) {
if (mInitialized) {
return;
}
mInitialized = true;
mContext = context;
- mPipNotification = new PipNotification(context, broadcastDispatcher,
- Optional.of(this).get());
+ mPipNotification = new PipNotification(context, this);
mPipBoundsHandler = pipBoundsHandler;
// Ensure that we have the display info in case we get calls to update the bounds before the
// listener calls back
@@ -248,15 +235,12 @@
mResizeAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
- mPipSurfaceTransactionHelper = pipSurfaceTransactionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTaskOrganizer.registerPipTransitionCallback(this);
mActivityTaskManager = ActivityTaskManager.getService();
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
- broadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter,
- null /* handler */, UserHandle.ALL);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL);
// Initialize the last orientation and apply the current configuration
Configuration initialConfig = mContext.getResources().getConfiguration();
@@ -265,10 +249,10 @@
mMediaSessionManager =
(MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
-
+ mWindowManagerShellWrapper = windowManagerShellWrapper;
try {
- WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
- } catch (RemoteException | UnsupportedOperationException e) {
+ mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
+ } catch (RemoteException e) {
Log.e(TAG, "Failed to register pinned stack listener", e);
}
}
@@ -344,7 +328,6 @@
mListeners.get(i).onPipActivityClosed();
}
mHandler.removeCallbacks(mClosePipRunnable);
- updatePipVisibility(false);
}
/**
@@ -358,7 +341,77 @@
mListeners.get(i).onMoveToFullscreen();
}
resizePinnedStack(STATE_NO_PIP);
- updatePipVisibility(false);
+ }
+
+ @Override
+ public void onActivityPinned(String packageName) {
+ if (DEBUG) Log.d(TAG, "onActivityPinned()");
+
+ RootTaskInfo taskInfo = getPinnedTaskInfo();
+ if (taskInfo == null) {
+ Log.w(TAG, "Cannot find pinned stack");
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo);
+ mPinnedStackId = taskInfo.taskId;
+ mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1];
+ mPipComponentName = ComponentName.unflattenFromString(
+ taskInfo.childTaskNames[taskInfo.childTaskNames.length - 1]);
+ // Set state to STATE_PIP so we show it when the pinned stack animation ends.
+ mState = STATE_PIP;
+ mMediaSessionManager.addOnActiveSessionsChangedListener(
+ mActiveMediaSessionListener, null);
+ updateMediaController(mMediaSessionManager.getActiveSessions(null));
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onPipEntered(packageName);
+ }
+ }
+
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean clearedTask) {
+ if (task.configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_PINNED) {
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
+
+ // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
+ movePipToFullscreen();
+ }
+
+ @Override
+ public void onTaskStackChanged() {
+ if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
+
+ if (getState() != STATE_NO_PIP) {
+ boolean hasPip = false;
+
+ RootTaskInfo taskInfo = getPinnedTaskInfo();
+ if (taskInfo == null || taskInfo.childTaskIds == null) {
+ Log.w(TAG, "There is nothing in pinned stack");
+ closePipInternal(false);
+ return;
+ }
+ for (int i = taskInfo.childTaskIds.length - 1; i >= 0; --i) {
+ if (taskInfo.childTaskIds[i] == mPipTaskId) {
+ // PIP task is still alive.
+ hasPip = true;
+ break;
+ }
+ }
+ if (!hasPip) {
+ // PIP task doesn't exist anymore in PINNED_STACK.
+ closePipInternal(true);
+ return;
+ }
+ }
+ if (getState() == STATE_PIP) {
+ if (mPipBounds != mDefaultPipBounds) {
+ mPipBounds = mDefaultPipBounds;
+ resizePinnedStack(STATE_PIP);
+ }
+ }
}
/**
@@ -603,80 +656,6 @@
return PLAYBACK_STATE_UNAVAILABLE;
}
- private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- @Override
- public void onTaskStackChanged() {
- if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
-
- if (getState() != STATE_NO_PIP) {
- boolean hasPip = false;
-
- RootTaskInfo taskInfo = getPinnedTaskInfo();
- if (taskInfo == null || taskInfo.childTaskIds == null) {
- Log.w(TAG, "There is nothing in pinned stack");
- closePipInternal(false);
- return;
- }
- for (int i = taskInfo.childTaskIds.length - 1; i >= 0; --i) {
- if (taskInfo.childTaskIds[i] == mPipTaskId) {
- // PIP task is still alive.
- hasPip = true;
- break;
- }
- }
- if (!hasPip) {
- // PIP task doesn't exist anymore in PINNED_STACK.
- closePipInternal(true);
- return;
- }
- }
- if (getState() == STATE_PIP) {
- if (mPipBounds != mDefaultPipBounds) {
- mPipBounds = mDefaultPipBounds;
- resizePinnedStack(STATE_PIP);
- }
- }
- }
-
- @Override
- public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
- if (DEBUG) Log.d(TAG, "onActivityPinned()");
-
- RootTaskInfo taskInfo = getPinnedTaskInfo();
- if (taskInfo == null) {
- Log.w(TAG, "Cannot find pinned stack");
- return;
- }
- if (DEBUG) Log.d(TAG, "PINNED_STACK:" + taskInfo);
- mPinnedStackId = taskInfo.taskId;
- mPipTaskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1];
- mPipComponentName = ComponentName.unflattenFromString(
- taskInfo.childTaskNames[taskInfo.childTaskNames.length - 1]);
- // Set state to STATE_PIP so we show it when the pinned stack animation ends.
- mState = STATE_PIP;
- mMediaSessionManager.addOnActiveSessionsChangedListener(
- mActiveMediaSessionListener, null);
- updateMediaController(mMediaSessionManager.getActiveSessions(null));
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onPipEntered(packageName);
- }
- updatePipVisibility(true);
- }
-
- @Override
- public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
- boolean clearedTask, boolean wasVisible) {
- if (task.configuration.windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_PINNED) {
- return;
- }
- if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
-
- // If PIPed activity is launched again by Launcher or intent, make it fullscreen.
- movePipToFullscreen();
- }
- };
-
@Override
public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) {
}
@@ -730,12 +709,6 @@
void onMediaControllerChanged();
}
- private void updatePipVisibility(final boolean visible) {
- Dependency.get(UiOffloadThread.class).execute(() -> {
- WindowManagerWrapper.getInstance().setPipVisibility(visible);
- });
- }
-
private String getStateDescription() {
if (mSuspendPipResizingReason == 0) {
return stateToName(mState);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
index 2f4df1f..8c04a52 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsViewController.java
@@ -147,9 +147,10 @@
return;
}
mPipOptional.ifPresent(pip -> {
- if (pip.getPlaybackState() == PipController.PLAYBACK_STATE_PAUSED) {
+ final int playbackState = pip.getPlaybackState();
+ if (playbackState == PipController.PLAYBACK_STATE_PAUSED) {
mMediaController.getTransportControls().play();
- } else if (pip.getPlaybackState() == PipController.PLAYBACK_STATE_PLAYING) {
+ } else if (playbackState == PipController.PLAYBACK_STATE_PLAYING) {
mMediaController.getTransportControls().pause();
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index 78569ed..0666811 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -32,11 +32,11 @@
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.PlaybackState;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.util.NotificationChannels;
import com.android.wm.shell.R;
@@ -163,8 +163,7 @@
}
};
- public PipNotification(Context context, BroadcastDispatcher broadcastDispatcher,
- PipController pipController) {
+ public PipNotification(Context context, PipController pipController) {
mPackageManager = context.getPackageManager();
mNotificationManager = (NotificationManager) context.getSystemService(
@@ -185,7 +184,7 @@
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_MENU);
intentFilter.addAction(ACTION_CLOSE);
- broadcastDispatcher.registerReceiver(mEventReceiver, intentFilter);
+ context.registerReceiver(mEventReceiver, intentFilter, UserHandle.USER_ALL);
onConfigurationChanged(context);
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipModule.java
index e8e3920..f094854 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/dagger/TvPipModule.java
@@ -19,7 +19,6 @@
import android.app.Activity;
import android.content.Context;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipBoundsHandler;
@@ -29,6 +28,7 @@
import com.android.systemui.pip.tv.PipController;
import com.android.systemui.pip.tv.PipMenuActivity;
import com.android.systemui.pip.tv.PipNotification;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -56,20 +56,18 @@
@SysUISingleton
@Provides
static Pip providePipController(Context context,
- BroadcastDispatcher broadcastDispatcher,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
- PipTaskOrganizer pipTaskOrganizer) {
- return new PipController(context, broadcastDispatcher, pipBoundsHandler,
- pipSurfaceTransactionHelper, pipTaskOrganizer);
+ PipTaskOrganizer pipTaskOrganizer,
+ WindowManagerShellWrapper windowManagerShellWrapper) {
+ return new PipController(context, pipBoundsHandler, pipTaskOrganizer,
+ windowManagerShellWrapper);
}
@SysUISingleton
@Provides
static PipNotification providePipNotification(Context context,
- BroadcastDispatcher broadcastDispatcher,
PipController pipController) {
- return new PipNotification(context, broadcastDispatcher, pipController);
+ return new PipNotification(context, pipController);
}
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 56b939d..a6c3221 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -51,6 +51,7 @@
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -72,6 +73,7 @@
private final Callback mCallback = new Callback();
private Dialog mDialog;
private boolean mWifiConnected;
+ private boolean mHotspotConnected;
@Inject
public CastTile(
@@ -84,7 +86,8 @@
QSLogger qsLogger,
CastController castController,
KeyguardStateController keyguardStateController,
- NetworkController networkController
+ NetworkController networkController,
+ HotspotController hotspotController
) {
super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
activityStarter, qsLogger);
@@ -95,6 +98,7 @@
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
mNetworkController.observe(this, mSignalCallback);
+ hotspotController.observe(this, mHotspotCallback);
}
@Override
@@ -222,7 +226,7 @@
}
state.icon = ResourceIcon.get(state.value ? R.drawable.ic_cast_connected
: R.drawable.ic_cast);
- if (mWifiConnected || state.value) {
+ if (canCastToWifi() || state.value) {
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
if (!state.value) {
state.secondaryLabel = "";
@@ -258,6 +262,10 @@
: mContext.getString(R.string.quick_settings_cast_device_default_name);
}
+ private boolean canCastToWifi() {
+ return mWifiConnected || mHotspotConnected;
+ }
+
private final NetworkController.SignalCallback mSignalCallback =
new NetworkController.SignalCallback() {
@Override
@@ -269,7 +277,25 @@
boolean enabledAndConnected = enabled && qsIcon.visible;
if (enabledAndConnected != mWifiConnected) {
mWifiConnected = enabledAndConnected;
- refreshState();
+ // Hotspot is not connected, so changes here should update
+ if (!mHotspotConnected) {
+ refreshState();
+ }
+ }
+ }
+ };
+
+ private final HotspotController.Callback mHotspotCallback =
+ new HotspotController.Callback() {
+ @Override
+ public void onHotspotChanged(boolean enabled, int numDevices) {
+ boolean enabledAndConnected = enabled && numDevices > 0;
+ if (enabledAndConnected != mHotspotConnected) {
+ mHotspotConnected = enabledAndConnected;
+ // Wifi is not connected, so changes here should update
+ if (!mWifiConnected) {
+ refreshState();
+ }
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index ed8da7c..f11683d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -102,6 +102,7 @@
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
+import java.util.function.Consumer;
import javax.inject.Inject;
@@ -142,6 +143,7 @@
private Region mActiveNavBarRegion;
+ private IPinnedStackAnimationListener mIPinnedStackAnimationListener;
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
private boolean mBound;
@@ -158,7 +160,6 @@
@VisibleForTesting
public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
-
@Override
public void startScreenPinning(int taskId) {
if (!verifyCaller("startScreenPinning")) {
@@ -232,21 +233,6 @@
}
@Override
- public void onSplitScreenInvoked() {
- if (!verifyCaller("onSplitScreenInvoked")) {
- return;
- }
- long token = Binder.clearCallingIdentity();
- try {
- mSplitScreenOptional.ifPresent(splitScreen -> {
- splitScreen.onDockedFirstAnimationFrame();
- });
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
- @Override
public void onOverviewShown(boolean fromHome) {
if (!verifyCaller("onOverviewShown")) {
return;
@@ -438,10 +424,11 @@
+ mHasPipFeature);
return;
}
+ mIPinnedStackAnimationListener = listener;
long token = Binder.clearCallingIdentity();
try {
mPipOptional.ifPresent(
- pip -> pip.setPinnedStackAnimationListener(listener));
+ pip -> pip.setPinnedStackAnimationListener(mPinnedStackAnimationCallback));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -607,6 +594,8 @@
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener =
this::notifySplitScreenBoundsChanged;
+ private final Consumer<Boolean> mPinnedStackAnimationCallback =
+ this::notifyPinnedStackAnimationStarted;
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
@@ -736,6 +725,17 @@
}
}
+ private void notifyPinnedStackAnimationStarted(Boolean isAnimationStarted) {
+ if (mIPinnedStackAnimationListener == null) {
+ return;
+ }
+ try {
+ mIPinnedStackAnimationListener.onPinnedStackAnimationStarted();
+ } catch (RemoteException e) {
+ Log.e(TAG_OPS, "Failed to call onPinnedStackAnimationStarted()", e);
+ }
+ }
+
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
boolean bouncerShowing) {
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
@@ -766,7 +766,7 @@
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
- mHandler.post(()-> {
+ mHandler.post(() -> {
mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
mInputFocusTransferStarted = false;
statusBarLazy.get().onInputFocusTransfer(false, true /* cancel */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index c1196d6..01aa53f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -189,7 +189,11 @@
blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
val zoomOut = blurUtils.ratioOfBlurRadius(blur)
try {
- wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut)
+ if (root.isAttachedToWindow) {
+ wallpaperManager.setWallpaperZoomOut(root.windowToken, zoomOut)
+ } else {
+ Log.i(TAG, "Won't set zoom. Window not attached $root")
+ }
} catch (e: IllegalArgumentException) {
Log.w(TAG, "Can't set zoom. Window is gone: ${root.windowToken}", e)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index c43ad36..82ad00a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -359,6 +360,11 @@
return false;
}
+ private static boolean isOngoingCallNotif(NotificationEntry entry) {
+ return entry.getSbn().isOngoing() && Notification.CATEGORY_CALL.equals(
+ entry.getSbn().getNotification().category);
+ }
+
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
@@ -391,6 +397,15 @@
return 1;
}
+ boolean selfCall = isOngoingCallNotif(mEntry);
+ boolean otherCall = isOngoingCallNotif(headsUpEntry.mEntry);
+
+ if (selfCall && !otherCall) {
+ return -1;
+ } else if (!selfCall && otherCall) {
+ return 1;
+ }
+
if (remoteInputActive && !headsUpEntry.remoteInputActive) {
return -1;
} else if (!remoteInputActive && headsUpEntry.remoteInputActive) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
index a29db4d..7aeca64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
@@ -21,7 +21,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.IntDef;
import android.annotation.UiThread;
@@ -36,7 +35,6 @@
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
-import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.statusbar.tv.TvStatusBar;
@@ -83,19 +81,14 @@
private static final int STATE_SHOWN = 2;
private static final int STATE_DISAPPEARING = 3;
- private static final int ANIMATION_DURATION = 600;
+ private static final int ANIMATION_DURATION_MS = 200;
private final Context mContext;
private boolean mIsEnabled;
private View mIndicatorView;
- private View mIconTextsContainer;
- private View mIconContainerBg;
- private View mIcon;
- private View mBgEnd;
- private View mTextsContainers;
- private TextView mTextView;
- private boolean mIsLtr;
+ private boolean mViewAndWindowAdded;
+ private ObjectAnimator mAnimator;
@State private int mState = STATE_STOPPED;
@@ -190,7 +183,7 @@
}
if (active) {
- showIfNotShown();
+ showIfNeeded();
} else {
hideIndicatorIfNeeded();
}
@@ -198,153 +191,132 @@
@UiThread
private void hideIndicatorIfNeeded() {
- // If not STATE_APPEARING, will check whether the indicator should be hidden when the
- // indicator comes to the STATE_SHOWN.
- // If STATE_DISAPPEARING or STATE_SHOWN - nothing else for us to do here.
- if (mState != STATE_SHOWN) return;
+ // If STOPPED, NOT_SHOWN or DISAPPEARING - nothing else for us to do here.
+ if (mState != STATE_SHOWN && mState != STATE_APPEARING) return;
- // If is in the STATE_SHOWN and there are no active recorders - hide.
- if (!hasActiveRecorders()) {
- hide();
+ if (hasActiveRecorders()) {
+ return;
+ }
+
+ if (mViewAndWindowAdded) {
+ mState = STATE_DISAPPEARING;
+ animateDisappearance();
+ } else {
+ // Appearing animation has not started yet, as we were still waiting for the View to be
+ // laid out.
+ mState = STATE_NOT_SHOWN;
+ removeIndicatorView();
}
}
@UiThread
- private void showIfNotShown() {
- if (mState != STATE_NOT_SHOWN) return;
+ private void showIfNeeded() {
+ // If STOPPED, SHOWN or APPEARING - nothing else for us to do here.
+ if (mState != STATE_NOT_SHOWN && mState != STATE_DISAPPEARING) return;
+
if (DEBUG) Log.d(TAG, "Showing indicator");
- mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
+ final int prevState = mState;
+ mState = STATE_APPEARING;
+
+ if (prevState == STATE_DISAPPEARING) {
+ animateAppearance();
+ return;
+ }
// Inflate the indicator view
mIndicatorView = LayoutInflater.from(mContext).inflate(
- R.layout.tv_audio_recording_indicator,
- null);
- mIconTextsContainer = mIndicatorView.findViewById(R.id.icon_texts_container);
- mIconContainerBg = mIconTextsContainer.findViewById(R.id.icon_container_bg);
- mIcon = mIconTextsContainer.findViewById(R.id.icon_mic);
- mTextsContainers = mIconTextsContainer.findViewById(R.id.texts_container);
- mTextView = mTextsContainers.findViewById(R.id.text);
- mBgEnd = mIndicatorView.findViewById(R.id.bg_end);
+ R.layout.tv_audio_recording_indicator, null);
- mTextsContainers.setVisibility(View.GONE);
- mIconContainerBg.setVisibility(View.GONE);
- mTextView.setVisibility(View.GONE);
- mBgEnd.setVisibility(View.GONE);
- mTextsContainers = null;
- mIconContainerBg = null;
- mTextView = null;
- mBgEnd = null;
-
- // Initially change the visibility to INVISIBLE, wait until and receives the size and
- // then animate it moving from "off" the screen correctly
- mIndicatorView.setVisibility(View.INVISIBLE);
+ // 1. Set alpha to 0.
+ // 2. Wait until the window is shown and the view is laid out.
+ // 3. Start a "fade in" (alpha) animation.
+ mIndicatorView.setAlpha(0f);
mIndicatorView
.getViewTreeObserver()
.addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
- if (mState == STATE_STOPPED) {
- return;
- }
+ // State could have changed to NOT_SHOWN (if all the recorders are
+ // already gone) to STOPPED (if the indicator was disabled)
+ if (mState != STATE_APPEARING) return;
+ mViewAndWindowAdded = true;
// Remove the observer
mIndicatorView.getViewTreeObserver().removeOnGlobalLayoutListener(
this);
- // Now that the width of the indicator has been assigned, we can
- // move it in from off the screen.
- final int initialOffset =
- (mIsLtr ? 1 : -1) * mIndicatorView.getWidth();
- final AnimatorSet set = new AnimatorSet();
- set.setDuration(ANIMATION_DURATION);
- set.playTogether(
- ObjectAnimator.ofFloat(mIndicatorView,
- View.TRANSLATION_X, initialOffset, 0),
- ObjectAnimator.ofFloat(mIndicatorView, View.ALPHA, 0f,
- 1f));
- set.addListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation,
- boolean isReverse) {
- if (mState == STATE_STOPPED) return;
-
- // Indicator is INVISIBLE at the moment, change it.
- mIndicatorView.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- onAppeared();
- }
- });
- set.start();
+ animateAppearance();
}
});
+ final boolean isLtr = mContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WRAP_CONTENT,
WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
- layoutParams.gravity = Gravity.TOP | (mIsLtr ? Gravity.RIGHT : Gravity.LEFT);
+ layoutParams.gravity = Gravity.TOP | (isLtr ? Gravity.RIGHT : Gravity.LEFT);
layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
layoutParams.packageName = mContext.getPackageName();
final WindowManager windowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
windowManager.addView(mIndicatorView, layoutParams);
-
- mState = STATE_APPEARING;
}
- @UiThread
- private void hide() {
- if (DEBUG) Log.d(TAG, "Hide indicator");
- final int targetOffset = (mIsLtr ? 1 : -1) * (mIndicatorView.getWidth()
- - (int) mIconTextsContainer.getTranslationX());
- final AnimatorSet set = new AnimatorSet();
- set.playTogether(
- ObjectAnimator.ofFloat(mIndicatorView, View.TRANSLATION_X, targetOffset),
- ObjectAnimator.ofFloat(mIcon, View.ALPHA, 0f));
- set.setDuration(ANIMATION_DURATION);
- set.addListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onHidden();
+ private void animateAppearance() {
+ animateAlphaTo(1f);
+ }
+
+ private void animateDisappearance() {
+ animateAlphaTo(0f);
+ }
+
+ private void animateAlphaTo(final float endValue) {
+ if (mAnimator == null) {
+ if (DEBUG) Log.d(TAG, "set up animator");
+
+ mAnimator = new ObjectAnimator();
+ mAnimator.setTarget(mIndicatorView);
+ mAnimator.setProperty(View.ALPHA);
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation, boolean isReverse) {
+ if (DEBUG) Log.d(TAG, "onAnimationStart");
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (DEBUG) Log.d(TAG, "onAnimationCancel");
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (DEBUG) Log.d(TAG, "onAnimationEnd");
+
+ if (mState == STATE_APPEARING) {
+ mState = STATE_SHOWN;
+ } else if (mState == STATE_DISAPPEARING) {
+ removeIndicatorView();
+ mState = STATE_NOT_SHOWN;
}
- });
- set.start();
-
- mState = STATE_DISAPPEARING;
- }
-
-
- @UiThread
- private void onAppeared() {
- if (mState == STATE_STOPPED) return;
-
- mState = STATE_SHOWN;
-
- hideIndicatorIfNeeded();
- }
-
- @UiThread
- private void onHidden() {
- if (mState == STATE_STOPPED) return;
-
- removeIndicatorView();
- mState = STATE_NOT_SHOWN;
-
- if (hasActiveRecorders()) {
- // Got new recorders, show again.
- showIfNotShown();
+ }
+ });
+ } else if (mAnimator.isRunning()) {
+ if (DEBUG) Log.d(TAG, "cancel running animation");
+ mAnimator.cancel();
}
+
+ final float currentValue = mIndicatorView.getAlpha();
+ if (DEBUG) Log.d(TAG, "animate alpha to " + endValue + " from " + currentValue);
+
+ mAnimator.setDuration((int) (Math.abs(currentValue - endValue) * ANIMATION_DURATION_MS));
+ mAnimator.setFloatValues(endValue);
+ mAnimator.start();
}
private boolean hasActiveRecorders() {
@@ -363,12 +335,9 @@
windowManager.removeView(mIndicatorView);
mIndicatorView = null;
- mIconTextsContainer = null;
- mIconContainerBg = null;
- mIcon = null;
- mTextsContainers = null;
- mTextView = null;
- mBgEnd = null;
+ mAnimator = null;
+
+ mViewAndWindowAdded = false;
}
private static List<String> splitByComma(String string) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index a6cd350..eb8f065 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -23,6 +23,7 @@
import android.view.LayoutInflater;
import android.view.View;
+import com.android.keyguard.KeyguardMessageArea;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
@@ -107,6 +108,11 @@
NotificationStackScrollLayout createNotificationStackScrollLayout();
/**
+ * Creates the KeyguardMessageArea.
+ */
+ KeyguardMessageArea createKeyguardMessageArea();
+
+ /**
* Creates the QSPanel.
*/
QSPanel createQSPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
index c7aa780fc..64f8dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java
@@ -23,20 +23,7 @@
* Utility class that handles view lifecycle events for View Controllers.
*
* Implementations should handle setup and teardown related activities inside of
- * {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on
- * any child controllers that this uses. This can be done in {@link init()} if the controllers
- * are injected, or right after creation time of the child controller.
- *
- * Tip: View "attachment" happens top down - parents are notified that they are attached before
- * any children. That means that if you call a method on a child controller in
- * {@link #onViewAttached()}, the child controller may not have had its onViewAttach method
- * called, so it may not be fully set up.
- *
- * As such, make sure that methods on your controller are safe to call _before_ its {@link #init()}
- * and {@link #onViewAttached()} methods are called. Specifically, if your controller must call
- * {@link View#findViewById(int)} on its root view to setup member variables, do so in its
- * constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding
- * listeners, dynamically changing content, or other runtime decisions.
+ * {@link #onViewAttached()} and {@link #onViewDetached()}.
*
* @param <T> View class that this ViewController is for.
*/
@@ -67,12 +54,10 @@
}
mInited = true;
- if (mView != null) {
- if (mView.isAttachedToWindow()) {
- mOnAttachStateListener.onViewAttachedToWindow(mView);
- }
- mView.addOnAttachStateChangeListener(mOnAttachStateListener);
+ if (mView.isAttachedToWindow()) {
+ mOnAttachStateListener.onViewAttachedToWindow(mView);
}
+ mView.addOnAttachStateChangeListener(mOnAttachStateListener);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 78f83d3..2081cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -122,8 +122,11 @@
static final int DIALOG_SAFETYWARNING_TIMEOUT_MILLIS = 5000;
static final int DIALOG_ODI_CAPTIONS_TOOLTIP_TIMEOUT_MILLIS = 5000;
static final int DIALOG_HOVERING_TIMEOUT_MILLIS = 16000;
- static final int DIALOG_SHOW_ANIMATION_DURATION = 300;
- static final int DIALOG_HIDE_ANIMATION_DURATION = 250;
+
+ private final int mDialogShowAnimationDurationMs;
+ private final int mDialogHideAnimationDurationMs;
+ private final boolean mShowLowMediaVolumeIcon;
+ private final boolean mChangeVolumeRowTintWhenInactive;
private final Context mContext;
private final H mHandler = new H();
@@ -154,8 +157,6 @@
private boolean mShowing;
private boolean mShowA11yStream;
- private final boolean mShowLowMediaVolumeIcon;
-
private int mActiveStream;
private int mPrevActiveStream;
private boolean mAutomute = VolumePrefs.DEFAULT_ENABLE_AUTOMUTE;
@@ -183,6 +184,12 @@
Prefs.getBoolean(context, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
mShowLowMediaVolumeIcon =
mContext.getResources().getBoolean(R.bool.config_showLowMediaVolumeIcon);
+ mChangeVolumeRowTintWhenInactive =
+ mContext.getResources().getBoolean(R.bool.config_changeVolumeRowTintWhenInactive);
+ mDialogShowAnimationDurationMs =
+ mContext.getResources().getInteger(R.integer.config_dialogShowAnimationDurationMs);
+ mDialogHideAnimationDurationMs =
+ mContext.getResources().getInteger(R.integer.config_dialogHideAnimationDurationMs);
}
@Override
@@ -272,7 +279,7 @@
mDialogView.animate()
.alpha(1)
.translationX(0)
- .setDuration(DIALOG_SHOW_ANIMATION_DURATION)
+ .setDuration(mDialogShowAnimationDurationMs)
.setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
.withEndAction(() -> {
if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
@@ -592,7 +599,7 @@
mODICaptionsTooltipView.setAlpha(0.f);
mODICaptionsTooltipView.animate()
.alpha(1.f)
- .setStartDelay(DIALOG_SHOW_ANIMATION_DURATION)
+ .setStartDelay(mDialogShowAnimationDurationMs)
.withEndAction(() -> {
if (D.BUG) Log.d(TAG, "tool:checkODICaptionsTooltip() putBoolean true");
Prefs.putBoolean(mContext,
@@ -614,7 +621,7 @@
mODICaptionsTooltipView.animate()
.alpha(0.f)
.setStartDelay(0)
- .setDuration(DIALOG_HIDE_ANIMATION_DURATION)
+ .setDuration(mDialogHideAnimationDurationMs)
.withEndAction(() -> mODICaptionsTooltipView.setVisibility(INVISIBLE))
.start();
}
@@ -793,7 +800,7 @@
mDialogView.setAlpha(1);
ViewPropertyAnimator animator = mDialogView.animate()
.alpha(0)
- .setDuration(DIALOG_HIDE_ANIMATION_DURATION)
+ .setDuration(mDialogHideAnimationDurationMs)
.setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
.withEndAction(() -> mHandler.postDelayed(() -> {
mDialog.dismiss();
@@ -1076,7 +1083,7 @@
iconRes = isStreamMuted(ss) ? R.drawable.ic_volume_media_bt_mute
: R.drawable.ic_volume_media_bt;
} else if (isStreamMuted(ss)) {
- iconRes = row.iconMuteRes;
+ iconRes = ss.muted ? R.drawable.ic_volume_media_off : row.iconMuteRes;
} else {
iconRes = mShowLowMediaVolumeIcon && ss.level * 2 < (ss.levelMax + ss.levelMin)
? R.drawable.ic_volume_media_low : row.iconRes;
@@ -1154,6 +1161,9 @@
row.slider.requestFocus();
}
boolean useActiveColoring = isActive && row.slider.isEnabled();
+ if (!useActiveColoring && !mChangeVolumeRowTintWhenInactive) {
+ return;
+ }
final ColorStateList tint = useActiveColoring
? Utils.getColorAccent(mContext)
: Utils.getColorAttr(mContext, android.R.attr.colorForeground);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index 524eca3..d6595b2 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -49,8 +49,6 @@
return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
}
- @SysUISingleton
- @Provides
static SplitScreen provideSplitScreen(Context context,
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController displayImeController, @Main Handler handler,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index a2d6ac8..250c592 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -16,24 +16,40 @@
package com.android.systemui.wmshell;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.ActivityTaskManager.RootTaskInfo;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pair;
import android.view.KeyEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.keyguard.ScreenLifecycle;
@@ -42,9 +58,12 @@
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.phone.PipUtils;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -70,8 +89,20 @@
@SysUISingleton
public final class WMShell extends SystemUI
implements CommandQueue.Callbacks, ProtoTraceable<SystemUiTraceProto> {
+ private static final String TAG = WMShell.class.getName();
+ private static final int INVALID_SYSUI_STATE_MASK =
+ SYSUI_STATE_GLOBAL_ACTIONS_SHOWING
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
+ | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+ | SYSUI_STATE_BOUNCER_SHOWING
+ | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
+ | SYSUI_STATE_BUBBLES_EXPANDED
+ | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
+
private final CommandQueue mCommandQueue;
+ private final ConfigurationController mConfigurationController;
private final DisplayImeController mDisplayImeController;
+ private final InputConsumerController mInputConsumerController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ActivityManagerWrapper mActivityManagerWrapper;
private final NavigationModeController mNavigationModeController;
@@ -84,12 +115,15 @@
// are non-optional windowing features like FULLSCREEN.
private final ShellTaskOrganizer mShellTaskOrganizer;
private final ProtoTracer mProtoTracer;
-
+ private boolean mIsSysUiStateValid;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
+ private KeyguardUpdateMonitorCallback mPipKeyguardCallback;
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@Inject
public WMShell(Context context, CommandQueue commandQueue,
+ ConfigurationController configurationController,
+ InputConsumerController inputConsumerController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
ActivityManagerWrapper activityManagerWrapper,
DisplayImeController displayImeController,
@@ -103,6 +137,8 @@
ProtoTracer protoTracer) {
super(context);
mCommandQueue = commandQueue;
+ mConfigurationController = configurationController;
+ mInputConsumerController = inputConsumerController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mActivityManagerWrapper = activityManagerWrapper;
mDisplayImeController = displayImeController;
@@ -140,6 +176,90 @@
pip.showPictureInPictureMenu();
}
});
+
+ mPipKeyguardCallback = new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (showing) {
+ pip.hidePipMenu(null, null);
+ }
+ }
+ };
+ mKeyguardUpdateMonitor.registerCallback(mPipKeyguardCallback);
+
+ mSysUiState.addCallback(sysUiStateFlag -> {
+ mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0;
+ pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag);
+ });
+
+ mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() {
+ @Override
+ public void onDensityOrFontScaleChanged() {
+ pip.onDensityOrFontScaleChanged();
+ }
+
+ @Override
+ public void onOverlayChanged() {
+ pip.onOverlayChanged();
+ }
+ });
+
+ // Handle for system task stack changes.
+ mActivityManagerWrapper.registerTaskStackListener(
+ new TaskStackChangeListener() {
+ @Override
+ public void onTaskStackChanged() {
+ pip.onTaskStackChanged();
+ }
+
+ @Override
+ public void onActivityPinned(String packageName, int userId, int taskId,
+ int stackId) {
+ pip.onActivityPinned(packageName);
+ mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
+ }
+
+ @Override
+ public void onActivityUnpinned() {
+ final Pair<ComponentName, Integer> topPipActivityInfo =
+ PipUtils.getTopPipActivity(
+ mContext, ActivityManager.getService());
+ final ComponentName topActivity = topPipActivityInfo.first;
+ pip.onActivityUnpinned(topActivity);
+ mInputConsumerController.unregisterInputConsumer();
+ }
+
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+ pip.onActivityRestartAttempt(task, clearedTask);
+ }
+ });
+
+ try {
+ RootTaskInfo taskInfo = ActivityTaskManager.getService().getRootTaskInfo(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
+ if (taskInfo != null) {
+ // If SystemUI restart, and it already existed a pinned stack,
+ // register the pip input consumer to ensure touch can send to it.
+ mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
+ }
+ } catch (RemoteException | UnsupportedOperationException e) {
+ Log.e(TAG, "Failed to register pinned stack listener", e);
+ e.printStackTrace();
+ }
+
+ // Register the listener for input consumer touch events. Only for Phone
+ if (pip.getPipTouchHandler() != null) {
+ mInputConsumerController.setInputListener(pip.getPipTouchHandler()::handleTouchEvent);
+ mInputConsumerController.setRegistrationListener(
+ pip.getPipTouchHandler()::onRegistrationChanged);
+ }
+
+ // The media session listener needs to be re-registered when switching users
+ UserInfoController userInfoController = Dependency.get(UserInfoController.class);
+ userInfoController.addCallback((String name, Drawable picture, String userAccount) ->
+ pip.registerSessionListenerForCurrentUser());
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 4038215..bf9df8e 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.wmshell;
+import android.app.IActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Handler;
@@ -29,7 +30,10 @@
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.pip.phone.PipAppOpsListener;
+import com.android.systemui.pip.phone.PipMediaController;
+import com.android.systemui.pip.phone.PipTouchHandler;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -72,12 +76,33 @@
@SysUISingleton
@Provides
+ static InputConsumerController provideInputConsumerController() {
+ return InputConsumerController.getPipInputConsumer();
+ }
+
+ @SysUISingleton
+ @Provides
static FloatingContentCoordinator provideFloatingContentCoordinator() {
return new FloatingContentCoordinator();
}
@SysUISingleton
@Provides
+ static PipAppOpsListener providesPipAppOpsListener(Context context,
+ IActivityManager activityManager,
+ PipTouchHandler pipTouchHandler) {
+ return new PipAppOpsListener(context, activityManager, pipTouchHandler.getMotionHelper());
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipMediaController providesPipMediaController(Context context,
+ IActivityManager activityManager) {
+ return new PipMediaController(context, activityManager);
+ }
+
+ @SysUISingleton
+ @Provides
static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger,
PackageManager packageManager) {
return new PipUiEventLogger(uiEventLogger, packageManager);
@@ -85,9 +110,8 @@
@SysUISingleton
@Provides
- static PipSurfaceTransactionHelper providesPipSurfaceTransactionHelper(Context context,
- ConfigurationController configController) {
- return new PipSurfaceTransactionHelper(context, configController);
+ static PipSurfaceTransactionHelper providesPipSurfaceTransactionHelper(Context context) {
+ return new PipSurfaceTransactionHelper(context);
}
@SysUISingleton
@@ -107,6 +131,12 @@
@SysUISingleton
@Provides
+ static WindowManagerShellWrapper provideWindowManagerShellWrapper() {
+ return new WindowManagerShellWrapper();
+ }
+
+ @SysUISingleton
+ @Provides
static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder(
DisplayMetrics displayMetrics) {
return new FlingAnimationUtils.Builder(displayMetrics);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 16fb2ca..6ed836c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -20,18 +20,18 @@
import android.os.Handler;
import android.view.IWindowManager;
-import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.Pip;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
+import com.android.systemui.pip.phone.PipAppOpsListener;
import com.android.systemui.pip.phone.PipController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.pip.phone.PipMediaController;
+import com.android.systemui.pip.phone.PipMenuActivityController;
+import com.android.systemui.pip.phone.PipTouchHandler;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -66,21 +66,17 @@
@SysUISingleton
@Provides
static Pip providePipController(Context context,
- BroadcastDispatcher broadcastDispatcher,
- ConfigurationController configController,
- DeviceConfigProxy deviceConfig,
DisplayController displayController,
- FloatingContentCoordinator floatingContentCoordinator,
- SysUiState sysUiState,
+ PipAppOpsListener pipAppOpsListener,
PipBoundsHandler pipBoundsHandler,
- PipSurfaceTransactionHelper surfaceTransactionHelper,
+ PipMediaController pipMediaController,
+ PipMenuActivityController pipMenuActivityController,
PipTaskOrganizer pipTaskOrganizer,
- PipUiEventLogger pipUiEventLogger) {
- return new PipController(context, broadcastDispatcher, configController, deviceConfig,
- displayController, floatingContentCoordinator, sysUiState, pipBoundsHandler,
- surfaceTransactionHelper,
- pipTaskOrganizer,
- pipUiEventLogger);
+ PipTouchHandler pipTouchHandler,
+ WindowManagerShellWrapper windowManagerShellWrapper) {
+ return new PipController(context, displayController,
+ pipAppOpsListener, pipBoundsHandler, pipMediaController, pipMenuActivityController,
+ pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper);
}
@SysUISingleton
@@ -101,6 +97,24 @@
@SysUISingleton
@Provides
+ static PipMenuActivityController providesPipMenuActivityController(Context context,
+ PipMediaController pipMediaController, PipTaskOrganizer pipTaskOrganizer) {
+ return new PipMenuActivityController(context, pipMediaController, pipTaskOrganizer);
+ }
+
+ @SysUISingleton
+ @Provides
+ static PipTouchHandler providesPipTouchHandler(Context context,
+ PipMenuActivityController menuActivityController, PipBoundsHandler pipBoundsHandler,
+ PipTaskOrganizer pipTaskOrganizer,
+ FloatingContentCoordinator floatingContentCoordinator,
+ PipUiEventLogger pipUiEventLogger) {
+ return new PipTouchHandler(context, menuActivityController, pipBoundsHandler,
+ pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger);
+ }
+
+ @SysUISingleton
+ @Provides
static PipTaskOrganizer providesPipTaskOrganizer(Context context,
PipBoundsHandler pipBoundsHandler,
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellWrapper.java b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellWrapper.java
new file mode 100644
index 0000000..178d472
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WindowManagerShellWrapper.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.wmshell;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.WindowConfiguration;
+import android.os.RemoteException;
+import android.view.WindowManagerGlobal;
+
+import com.android.systemui.shared.system.PinnedStackListenerForwarder;
+
+/**
+ * The singleton wrapper to communicate between WindowManagerService and WMShell features
+ * (e.g: PIP, SplitScreen, Bubble, OneHandedMode...etc)
+ */
+public class WindowManagerShellWrapper {
+ private static final String TAG = WindowManagerShellWrapper.class.getSimpleName();
+
+ public static final int WINDOWING_MODE_PINNED = WindowConfiguration.WINDOWING_MODE_PINNED;
+
+ /**
+ * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive
+ * updates from the window manager service.
+ */
+ private PinnedStackListenerForwarder mPinnedStackListenerForwarder =
+ new PinnedStackListenerForwarder();
+
+ /**
+ * Adds a pinned stack listener, which will receive updates from the window manager service
+ * along with any other pinned stack listeners that were added via this method.
+ */
+ public void addPinnedStackListener(PinnedStackListenerForwarder.PinnedStackListener listener)
+ throws
+ RemoteException {
+ mPinnedStackListenerForwarder.addListener(listener);
+ WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener(
+ DEFAULT_DISPLAY, mPinnedStackListenerForwarder);
+ }
+
+ /**
+ * Removes a pinned stack listener.
+ */
+ public void removePinnedStackListener(
+ PinnedStackListenerForwarder.PinnedStackListener listener) {
+ mPinnedStackListenerForwarder.removeListener(listener);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index dffad6c..9be2d12 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -41,6 +41,8 @@
import android.testing.ViewUtils;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
@@ -65,7 +67,7 @@
private ComponentName mComponentName;
private Intent mServiceIntent;
private TestableLooper mTestableLooper;
- private KeyguardSecurityContainer mKeyguardSecurityContainer;
+ private ViewGroup mParent;
@Mock
private Handler mHandler;
@@ -82,8 +84,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
- ViewUtils.attachView(mKeyguardSecurityContainer);
+ mParent = spy(new FrameLayout(mContext));
+ ViewUtils.attachView(mParent);
mTestableLooper = TestableLooper.get(this);
mComponentName = new ComponentName(mContext, "FakeKeyguardClient.class");
@@ -94,14 +96,13 @@
when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient);
when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient);
- mTestController = new AdminSecondaryLockScreenController.Factory(
- mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler)
- .create(mKeyguardCallback);
+ mTestController = new AdminSecondaryLockScreenController(
+ mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler);
}
@After
public void tearDown() {
- ViewUtils.detachView(mKeyguardSecurityContainer);
+ ViewUtils.detachView(mParent);
}
@Test
@@ -145,7 +146,7 @@
SurfaceView v = verifySurfaceReady();
mTestController.hide();
- verify(mKeyguardSecurityContainer).removeView(v);
+ verify(mParent).removeView(v);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
@@ -153,7 +154,7 @@
public void testHide_notShown() throws Exception {
mTestController.hide();
// Nothing should happen if trying to hide when the view isn't attached yet.
- verify(mKeyguardSecurityContainer, never()).removeView(any(SurfaceView.class));
+ verify(mParent, never()).removeView(any(SurfaceView.class));
}
@Test
@@ -181,7 +182,7 @@
private SurfaceView verifySurfaceReady() throws Exception {
mTestableLooper.processAllMessages();
ArgumentCaptor<SurfaceView> captor = ArgumentCaptor.forClass(SurfaceView.class);
- verify(mKeyguardSecurityContainer).addView(captor.capture());
+ verify(mParent).addView(captor.capture());
mTestableLooper.processAllMessages();
verify(mKeyguardClient).onCreateKeyguardSurface(any(), any(IKeyguardCallback.class));
@@ -189,7 +190,7 @@
}
private void verifyViewDismissed(SurfaceView v) throws Exception {
- verify(mKeyguardSecurityContainer).removeView(v);
+ verify(mParent).removeView(v);
verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true);
assertThat(mContext.isBound(mComponentName)).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
deleted file mode 100644
index c2ade81..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.any;
-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;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.KeyEvent;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase {
-
- @Mock
- private KeyguardAbsKeyInputView mAbsKeyInputView;
- @Mock
- private PasswordTextView mPasswordEntry;
- @Mock
- private KeyguardMessageArea mKeyguardMessageArea;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private SecurityMode mSecurityMode;
- @Mock
- private LockPatternUtils mLockPatternUtils;
- @Mock
- private KeyguardSecurityCallback mKeyguardSecurityCallback;
- @Mock
- private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
- @Mock
- private KeyguardMessageAreaController mKeyguardMessageAreaController;
- @Mock
- private LatencyTracker mLatencyTracker;
-
- private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
- .thenReturn(mKeyguardMessageAreaController);
- when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1);
- when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry);
- when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true);
- when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area))
- .thenReturn(mKeyguardMessageArea);
- mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView,
- mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
- mKeyguardMessageAreaControllerFactory, mLatencyTracker) {
- @Override
- void resetState() {
- }
-
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- }
- };
- mKeyguardAbsKeyInputViewController.init();
- reset(mKeyguardMessageAreaController); // Clear out implicit call to init.
- }
-
- @Test
- public void onKeyDown_clearsSecurityMessage() {
- ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
- ArgumentCaptor.forClass(KeyDownListener.class);
- verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
- onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
- KeyEvent.KEYCODE_0, mock(KeyEvent.class));
- verify(mKeyguardSecurityCallback).userActivity();
- verify(mKeyguardMessageAreaController).setMessage(eq(""));
- }
-
- @Test
- public void onKeyDown_noSecurityMessageInteraction() {
- ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor =
- ArgumentCaptor.forClass(KeyDownListener.class);
- verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture());
- onKeyDownListenerArgumentCaptor.getValue().onKeyDown(
- KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
- verifyZeroInteractions(mKeyguardSecurityCallback);
- verifyZeroInteractions(mKeyguardMessageAreaController);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index e793079..5999e2c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -41,9 +41,11 @@
import android.widget.TextClock;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -76,7 +78,12 @@
when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area))
.thenReturn(mMockKeyguardSliceView);
- LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+ InjectionInflationController inflationController = new InjectionInflationController(
+ SystemUIFactory.getInstance()
+ .getSysUIComponent()
+ .createViewInstanceCreatorFactory());
+ LayoutInflater layoutInflater = inflationController
+ .injectable(LayoutInflater.from(getContext()));
layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
deleted file mode 100644
index a7197cc..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
- @Mock
- private ConfigurationController mConfigurationController;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private KeyguardMessageArea mKeyguardMessageArea;
-
- private KeyguardMessageAreaController mMessageAreaController;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mMessageAreaController = new KeyguardMessageAreaController.Factory(
- mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea);
- }
-
- @Test
- public void onAttachedToWindow_registersConfigurationCallback() {
- ArgumentCaptor<ConfigurationListener> configurationListenerArgumentCaptor =
- ArgumentCaptor.forClass(ConfigurationListener.class);
-
- mMessageAreaController.onViewAttached();
- verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture());
-
- mMessageAreaController.onViewDetached();
- verify(mConfigurationController).removeCallback(
- eq(configurationListenerArgumentCaptor.getValue()));
- }
-
- @Test
- public void onAttachedToWindow_registersKeyguardUpdateMontiorCallback() {
- ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
-
- mMessageAreaController.onViewAttached();
- verify(mKeyguardUpdateMonitor).registerCallback(
- keyguardUpdateMonitorCallbackArgumentCaptor.capture());
-
- mMessageAreaController.onViewDetached();
- verify(mKeyguardUpdateMonitor).removeCallback(
- eq(keyguardUpdateMonitorCallbackArgumentCaptor.getValue()));
- }
-
- @Test
- public void testClearsTextField() {
- mMessageAreaController.setMessage("");
- verify(mKeyguardMessageArea).setMessage("");
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
index 31fb25a..fc7b9a4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -11,60 +11,65 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License.
+ * limitations under the License
*/
package com.android.keyguard;
-import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class KeyguardMessageAreaTest extends SysuiTestCase {
- private KeyguardMessageArea mKeyguardMessageArea;
+ @Mock
+ private ConfigurationController mConfigurationController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private KeyguardMessageArea mMessageArea;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mKeyguardMessageArea = new KeyguardMessageArea(mContext, null);
- mKeyguardMessageArea.setBouncerVisible(true);
+ mMessageArea = new KeyguardMessageArea(mContext, null, mKeyguardUpdateMonitor,
+ mConfigurationController);
+ waitForIdleSync();
}
@Test
- public void testShowsTextField() {
- mKeyguardMessageArea.setVisibility(View.INVISIBLE);
- mKeyguardMessageArea.setMessage("oobleck");
- assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.VISIBLE);
- assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
+ public void onAttachedToWindow_registersConfigurationCallback() {
+ mMessageArea.onAttachedToWindow();
+ verify(mConfigurationController).addCallback(eq(mMessageArea));
+
+ mMessageArea.onDetachedFromWindow();
+ verify(mConfigurationController).removeCallback(eq(mMessageArea));
}
@Test
- public void testHiddenWhenBouncerHidden() {
- mKeyguardMessageArea.setBouncerVisible(false);
- mKeyguardMessageArea.setVisibility(View.INVISIBLE);
- mKeyguardMessageArea.setMessage("oobleck");
- assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
- assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck");
+ public void clearFollowedByMessage_keepsMessage() {
+ mMessageArea.setMessage("");
+ mMessageArea.setMessage("test");
+
+ CharSequence[] messageText = new CharSequence[1];
+ messageText[0] = mMessageArea.getText();
+
+ assertEquals("test", messageText[0]);
}
- @Test
- public void testClearsTextField() {
- mKeyguardMessageArea.setVisibility(View.VISIBLE);
- mKeyguardMessageArea.setMessage("");
- assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE);
- assertThat(mKeyguardMessageArea.getText()).isEqualTo("");
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
deleted file mode 100644
index c69ec1a..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard
-
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.internal.util.LatencyTracker
-import com.android.internal.widget.LockPatternUtils
-import com.android.internal.widget.LockPatternView
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class KeyguardPatternViewControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var mKeyguardPatternView: KeyguardPatternView
- @Mock
- private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock
- private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode
- @Mock
- private lateinit var mLockPatternUtils: LockPatternUtils
- @Mock
- private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
- @Mock
- private lateinit var mLatencyTracker: LatencyTracker
- @Mock
- private lateinit
- var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
- @Mock
- private lateinit var mKeyguardMessageArea: KeyguardMessageArea
- @Mock
- private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController
- @Mock
- private lateinit var mLockPatternView: LockPatternView
-
- private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
- `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area))
- .thenReturn(mKeyguardMessageArea)
- `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
- .thenReturn(mLockPatternView)
- `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
- .thenReturn(mKeyguardMessageAreaController)
- mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView,
- mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
- mLatencyTracker, mKeyguardMessageAreaControllerFactory)
- }
-
- @Test
- fun onPause_clearsTextField() {
- mKeyguardPatternViewController.init()
- mKeyguardPatternViewController.onPause()
- verify(mKeyguardMessageAreaController).setMessage("")
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
new file mode 100644
index 0000000..b4363cf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard
+
+import androidx.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.LayoutInflater
+
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPatternViewTest : SysuiTestCase() {
+
+ private lateinit var mKeyguardPatternView: KeyguardPatternView
+ private lateinit var mSecurityMessage: KeyguardMessageArea
+
+ @Before
+ fun setup() {
+ val inflater = LayoutInflater.from(context)
+ mDependency.injectMockDependency(KeyguardUpdateMonitor::class.java)
+ mKeyguardPatternView = inflater.inflate(R.layout.keyguard_pattern_view, null)
+ as KeyguardPatternView
+ mSecurityMessage = KeyguardMessageArea(mContext, null,
+ mock(KeyguardUpdateMonitor::class.java), mock(ConfigurationController::class.java))
+ mKeyguardPatternView.mSecurityMessageDisplay = mSecurityMessage
+ }
+
+ @Test
+ fun onPause_clearsTextField() {
+ mSecurityMessage.setMessage("an old message")
+ mKeyguardPatternView.onPause()
+ assertThat(mSecurityMessage.text).isEqualTo("")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
deleted file mode 100644
index 4944284..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper.RunWithLooper;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.util.LatencyTracker;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
-
- @Mock
- private KeyguardPinBasedInputView mPinBasedInputView;
- @Mock
- private PasswordTextView mPasswordEntry;
- @Mock
- private KeyguardMessageArea mKeyguardMessageArea;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private SecurityMode mSecurityMode;
- @Mock
- private LockPatternUtils mLockPatternUtils;
- @Mock
- private KeyguardSecurityCallback mKeyguardSecurityCallback;
- @Mock
- private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory;
- @Mock
- private KeyguardMessageAreaController mKeyguardMessageAreaController;
- @Mock
- private LatencyTracker mLatencyTracker;
- @Mock
- private LiftToActivateListener mLiftToactivateListener;
- @Mock
- private View mDeleteButton;
- @Mock
- private View mOkButton;
-
- private KeyguardPinBasedInputViewController mKeyguardPinViewController;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class)))
- .thenReturn(mKeyguardMessageAreaController);
- when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1);
- when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry);
- when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true);
- when(mPinBasedInputView.findViewById(R.id.keyguard_message_area))
- .thenReturn(mKeyguardMessageArea);
- when(mPinBasedInputView.findViewById(R.id.delete_button))
- .thenReturn(mDeleteButton);
- when(mPinBasedInputView.findViewById(R.id.key_enter))
- .thenReturn(mOkButton);
- mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
- mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
- mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) {
- @Override
- public void onResume(int reason) {
- super.onResume(reason);
- }
- };
- mKeyguardPinViewController.init();
- }
-
- @Test
- public void onResume_requestsFocus() {
- mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON);
- verify(mPasswordEntry).requestFocus();
- }
-}
-
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
new file mode 100644
index 0000000..6666a92
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class KeyguardPinBasedInputViewTest extends SysuiTestCase {
+
+ @Mock
+ private PasswordTextView mPasswordEntry;
+ @Mock
+ private SecurityMessageDisplay mSecurityMessageDisplay;
+ @InjectMocks
+ private KeyguardPinBasedInputView mKeyguardPinView;
+
+ @Before
+ public void setup() {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+ mKeyguardPinView =
+ (KeyguardPinBasedInputView) inflater.inflate(R.layout.keyguard_pin_view, null);
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void onResume_requestsFocus() {
+ mKeyguardPinView.onResume(KeyguardSecurityView.SCREEN_ON);
+ verify(mPasswordEntry).requestFocus();
+ }
+
+ @Test
+ public void onKeyDown_clearsSecurityMessage() {
+ mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_0, mock(KeyEvent.class));
+ verify(mSecurityMessageDisplay).setMessage(eq(""));
+ }
+
+ @Test
+ public void onKeyDown_noSecurityMessageInteraction() {
+ mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class));
+ verifyZeroInteractions(mSecurityMessageDisplay);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index ae159c7..559284a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -31,7 +31,9 @@
import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.InjectionInflationController;
import org.junit.After;
import org.junit.Before;
@@ -63,6 +65,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext);
when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
@@ -74,7 +77,11 @@
allowTestableLooperAsMainThread();
- mLayoutInflater = LayoutInflater.from(mContext);
+ InjectionInflationController inflationController = new InjectionInflationController(
+ SystemUIFactory.getInstance()
+ .getSysUIComponent()
+ .createViewInstanceCreatorFactory());
+ mLayoutInflater = inflationController.injectable(LayoutInflater.from(mContext));
mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
deleted file mode 100644
index cdb91ec..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.WindowInsetsController;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper()
-public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
-
- @Rule
- public MockitoRule mRule = MockitoJUnit.rule();
-
- @Mock
- private KeyguardSecurityContainer mView;
- @Mock
- private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory;
- @Mock
- private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController;
- @Mock
- private LockPatternUtils mLockPatternUtils;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private KeyguardSecurityModel mKeyguardSecurityModel;
- @Mock
- private MetricsLogger mMetricsLogger;
- @Mock
- private UiEventLogger mUiEventLogger;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- private KeyguardInputViewController mInputViewController;
- @Mock
- private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
- @Mock
- private WindowInsetsController mWindowInsetsController;
- @Mock
- private KeyguardSecurityViewFlipper mSecurityViewFlipper;
- @Mock
- private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
-
- private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
-
- @Before
- public void setup() {
- when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
- .thenReturn(mAdminSecondaryLockScreenController);
- when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
-
- mKeyguardSecurityContainerController = new KeyguardSecurityContainerController(
- mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
- mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
- mKeyguardStateController, mKeyguardSecurityViewFlipperController);
-
- mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
- }
-
- @Test
- public void showSecurityScreen_canInflateAllModes() {
- SecurityMode[] modes = SecurityMode.values();
- for (SecurityMode mode : modes) {
- when(mInputViewController.getSecurityMode()).thenReturn(mode);
- mKeyguardSecurityContainerController.showSecurityScreen(mode);
- if (mode == SecurityMode.Invalid) {
- verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView(
- any(SecurityMode.class), any(KeyguardSecurityCallback.class));
- } else {
- verify(mKeyguardSecurityViewFlipperController).getSecurityView(
- eq(mode), any(KeyguardSecurityCallback.class));
- }
- }
- }
-
- @Test
- public void startDisappearAnimation_animatesKeyboard() {
- when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
- SecurityMode.Password);
- when(mInputViewController.getSecurityMode()).thenReturn(
- SecurityMode.Password);
- when(mKeyguardSecurityViewFlipperController.getSecurityView(
- eq(SecurityMode.Password), any(KeyguardSecurityCallback.class)))
- .thenReturn(mInputViewController);
- mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
-
- mKeyguardSecurityContainerController.startDisappearAnimation(null);
- verify(mInputViewController).startDisappearAnimation(eq(null));
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 854be1f..a867825 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -19,19 +19,23 @@
import static android.view.WindowInsets.Type.ime;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.LayoutInflater;
import android.view.WindowInsetsController;
import androidx.test.filters.SmallTest;
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Rule;
@@ -46,26 +50,68 @@
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
- @Rule
- public MockitoRule mRule = MockitoJUnit.rule();
-
+ @Mock
+ private KeyguardSecurityModel mKeyguardSecurityModel;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock
+ private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
+ @Mock
+ private KeyguardSecurityView mSecurityView;
@Mock
private WindowInsetsController mWindowInsetsController;
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
-
+ @Rule
+ public MockitoRule mRule = MockitoJUnit.rule();
private KeyguardSecurityContainer mKeyguardSecurityContainer;
@Before
public void setup() {
- when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
- mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
+ mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController);
+ mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
+ mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
+ mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()) {
+ @Override
+ protected KeyguardSecurityView getSecurityView(
+ KeyguardSecurityModel.SecurityMode securityMode) {
+ return mSecurityView;
+ }
+ };
mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
+ when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
+ mKeyguardSecurityContainer.setSecurityCallback(mSecurityCallback);
+ }
+
+ @Test
+ public void showSecurityScreen_canInflateAllModes() {
+ Context context = getContext();
+
+ for (int theme : new int[] {R.style.Theme_SystemUI, R.style.Theme_SystemUI_Light}) {
+ context.setTheme(theme);
+ final LayoutInflater inflater = LayoutInflater.from(context);
+ KeyguardSecurityModel.SecurityMode[] modes =
+ KeyguardSecurityModel.SecurityMode.values();
+ for (KeyguardSecurityModel.SecurityMode mode : modes) {
+ final int resId = mKeyguardSecurityContainer.getLayoutIdFor(mode);
+ if (resId == 0) {
+ continue;
+ }
+ inflater.inflate(resId, null /* root */, false /* attach */);
+ }
+ }
}
@Test
public void startDisappearAnimation_animatesKeyboard() {
- mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password);
+ when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
+ KeyguardSecurityModel.SecurityMode.Password);
+ mKeyguardSecurityContainer.showPrimarySecurityScreen(false /* turningOff */);
+
+ mKeyguardSecurityContainer.startDisappearAnimation(null);
+ verify(mSecurityView).startDisappearAnimation(eq(null));
verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(),
any(), any());
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
deleted file mode 100644
index 3b7f4b8..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-import android.view.WindowInsetsController;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper()
-public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
-
- @Rule
- public MockitoRule mRule = MockitoJUnit.rule();
-
- @Mock
- private KeyguardSecurityViewFlipper mView;
- @Mock
- private LayoutInflater mLayoutInflater;
- @Mock
- private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
- @Mock
- private KeyguardInputViewController mKeyguardInputViewController;
- @Mock
- private KeyguardInputView mInputView;
- @Mock
- private WindowInsetsController mWindowInsetsController;
- @Mock
- private KeyguardSecurityCallback mKeyguardSecurityCallback;
-
- private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
-
- @Before
- public void setup() {
- when(mKeyguardSecurityViewControllerFactory.create(
- any(KeyguardInputView.class), any(SecurityMode.class),
- any(KeyguardSecurityCallback.class)))
- .thenReturn(mKeyguardInputViewController);
- when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
-
- mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView,
- mLayoutInflater, mKeyguardSecurityViewControllerFactory);
- }
-
- @Test
- public void showSecurityScreen_canInflateAllModes() {
- SecurityMode[] modes = SecurityMode.values();
- // Always return an invalid controller so that we're always making a new one.
- when(mKeyguardInputViewController.getSecurityMode()).thenReturn(SecurityMode.Invalid);
- for (SecurityMode mode : modes) {
- reset(mLayoutInflater);
- when(mLayoutInflater.inflate(anyInt(), eq(mView), eq(false)))
- .thenReturn(mInputView);
- mKeyguardSecurityViewFlipperController.getSecurityView(mode, mKeyguardSecurityCallback);
- if (mode == SecurityMode.Invalid || mode == SecurityMode.None) {
- verify(mLayoutInflater, never()).inflate(
- anyInt(), any(ViewGroup.class), anyBoolean());
- } else {
- verify(mLayoutInflater).inflate(anyInt(), eq(mView), eq(false));
- }
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
index 79ec4f2..0431704 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -24,7 +24,9 @@
import android.view.LayoutInflater;
import com.android.systemui.R;
+import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.InjectionInflationController;
import org.junit.Before;
import org.junit.Test;
@@ -48,7 +50,13 @@
@Before
public void setUp() {
allowTestableLooperAsMainThread();
- LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+ mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
+ InjectionInflationController inflationController = new InjectionInflationController(
+ SystemUIFactory.getInstance()
+ .getSysUIComponent()
+ .createViewInstanceCreatorFactory());
+ LayoutInflater layoutInflater = inflationController
+ .injectable(LayoutInflater.from(getContext()));
mKeyguardStatusView =
(KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null);
org.mockito.MockitoAnnotations.initMocks(this);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index 66656c5..f6b39c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -45,6 +45,7 @@
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
@@ -119,8 +120,11 @@
when(mDialog2.isAllowDeviceCredentials()).thenReturn(false);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
- FingerprintSensorProperties prop = new FingerprintSensorProperties(
- 1, FingerprintSensorProperties.TYPE_UDFPS, true, 1);
+ FingerprintSensorProperties prop = new FingerprintSensorProperties(1 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 1 /* maxEnrollmentsPerUser */,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ true /* resetLockoutRequireHardwareAuthToken */);
List<FingerprintSensorProperties> props = new ArrayList<>();
props.add(prop);
when(mFingerprintManager.getSensorProperties()).thenReturn(props);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index f65c2c9..2f8d3f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -92,6 +92,7 @@
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import com.google.common.collect.ImmutableList;
@@ -191,6 +192,8 @@
private LauncherApps mLauncherApps;
@Mock private LockscreenLockIconController mLockIconController;
+ @Mock private WindowManagerShellWrapper mWindowManagerShellWrapper;
+
private BubbleData mBubbleData;
private TestableLooper mTestableLooper;
@@ -269,6 +272,7 @@
mock(INotificationManager.class),
mStatusBarService,
mWindowManager,
+ mWindowManagerShellWrapper,
mLauncherApps);
mBubbleController.setExpandListener(mBubbleExpandListener);
@@ -1007,6 +1011,29 @@
verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
}
+
+ /**
+ * Verifies that when a non visually interruptive update occurs for a bubble in the overflow,
+ * the that bubble does not get promoted from the overflow.
+ */
+ @Test
+ public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() {
+ // Setup
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mRow2.getEntry());
+ assertTrue(mBubbleController.hasBubbles());
+
+ // Overflow it
+ mBubbleData.dismissBubbleWithKey(mRow.getEntry().getKey(),
+ BubbleController.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse();
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())).isTrue();
+
+ // Test
+ mBubbleController.updateBubble(mRow.getEntry());
+ assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse();
+ }
+
/**
* Sets the bubble metadata flags for this entry. These ]flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 315caee..4bbc41e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -513,6 +513,26 @@
}
/**
+ * Verifies that when a non visually interruptive update occurs, that the selection does not
+ * change.
+ */
+ @Test
+ public void test_notVisuallyInterruptive_updateBubble_selectionDoesntChange() {
+ // Setup
+ sendUpdatedEntryAtTime(mEntryA1, 1000);
+ sendUpdatedEntryAtTime(mEntryB1, 2000);
+ sendUpdatedEntryAtTime(mEntryB2, 3000);
+ sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1]
+ mBubbleData.setListener(mListener);
+
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA2);
+
+ // Test
+ sendUpdatedEntryAtTime(mEntryB1, 5000, false /* isVisuallyInterruptive */);
+ assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA2);
+ }
+
+ /**
* Verifies that a request to expand the stack has no effect if there are no bubbles.
*/
@Test
@@ -883,9 +903,15 @@
}
private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
+ sendUpdatedEntryAtTime(entry, postTime, true /* visuallyInterruptive */);
+ }
+
+ private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime,
+ boolean visuallyInterruptive) {
setPostTime(entry, postTime);
// BubbleController calls this:
Bubble b = mBubbleData.getOrCreateBubble(entry, null /* persistedBubble */);
+ b.setVisuallyInterruptiveForTest(visuallyInterruptive);
// And then this
mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/,
true /* showInShade */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index dd191e9..cfbd398 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -92,6 +92,7 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import org.junit.Before;
import org.junit.Ignore;
@@ -185,6 +186,8 @@
private IStatusBarService mStatusBarService;
@Mock
private LauncherApps mLauncherApps;
+ @Mock
+ private WindowManagerShellWrapper mWindowManagerShellWrapper;
private BubbleData mBubbleData;
@@ -271,6 +274,7 @@
mock(INotificationManager.class),
mStatusBarService,
mWindowManager,
+ mWindowManagerShellWrapper,
mLauncherApps);
mBubbleController.addNotifCallback(mNotifCallback);
mBubbleController.setExpandListener(mBubbleExpandListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index 58b27f2..ec9571a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -36,6 +36,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
/**
* Testable BubbleController subclass that immediately synchronizes surfaces.
@@ -63,6 +64,7 @@
INotificationManager notificationManager,
IStatusBarService statusBarService,
WindowManager windowManager,
+ WindowManagerShellWrapper windowManagerShellWrapper,
LauncherApps launcherApps) {
super(context,
notificationShadeWindowController, statusBarStateController, shadeController,
@@ -70,7 +72,7 @@
zenModeController, lockscreenUserManager, groupManager, entryManager,
notifPipeline, featureFlags, dumpManager, floatingContentCoordinator,
dataRepository, sysUiState, notificationManager, statusBarService,
- windowManager, launcherApps);
+ windowManager, windowManagerShellWrapper, launcherApps);
setInflateSynchronously(true);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 8a30b00..81139f19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -43,7 +43,6 @@
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
@@ -53,7 +52,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.anyBoolean
@@ -203,7 +201,7 @@
fun bindWhenUnattached() {
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, null, null, device, true, null)
- player.bind(state)
+ player.bind(state, PACKAGE)
assertThat(player.isPlaying()).isFalse()
}
@@ -212,7 +210,7 @@
player.attach(holder)
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
- player.bind(state)
+ player.bind(state, PACKAGE)
assertThat(appName.getText()).isEqualTo(APP)
assertThat(titleText.getText()).isEqualTo(TITLE)
assertThat(artistText.getText()).isEqualTo(ARTIST)
@@ -223,7 +221,7 @@
player.attach(holder)
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
- player.bind(state)
+ player.bind(state, PACKAGE)
val list = ArgumentCaptor.forClass(ColorStateList::class.java)
verify(view).setBackgroundTintList(list.capture())
assertThat(list.value).isEqualTo(ColorStateList.valueOf(BG_COLOR))
@@ -234,7 +232,7 @@
player.attach(holder)
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null)
- player.bind(state)
+ player.bind(state, PACKAGE)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
assertThat(seamless.isEnabled()).isTrue()
}
@@ -246,7 +244,7 @@
player.attach(holder)
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, disabledDevice, true, null)
- player.bind(state)
+ player.bind(state, PACKAGE)
verify(expandedSet).setVisibility(seamless.id, View.GONE)
verify(expandedSet).setVisibility(seamlessFallback.id, View.VISIBLE)
verify(collapsedSet).setVisibility(seamless.id, View.GONE)
@@ -258,7 +256,7 @@
player.attach(holder)
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
- player.bind(state)
+ player.bind(state, PACKAGE)
assertThat(seamless.isEnabled()).isTrue()
assertThat(seamlessText.getText()).isEqualTo(context.getResources().getString(
com.android.internal.R.string.ext_media_seamless_action))
@@ -270,7 +268,7 @@
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, device, true, null,
resumption = true)
- player.bind(state)
+ player.bind(state, PACKAGE)
assertThat(seamlessText.getText()).isEqualTo(DEVICE_NAME)
assertThat(seamless.isEnabled()).isFalse()
}
@@ -322,31 +320,18 @@
@Test
fun dismissButtonClick() {
+ val mediaKey = "key for dismissal"
player.attach(holder)
val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null,
notificationKey = KEY)
- player.bind(state)
+ player.bind(state, mediaKey)
dismiss.callOnClick()
val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java)
verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean())
captor.value.onDismiss()
- verify(mediaDataManager).dismissMediaData(eq(KEY), anyLong())
- }
-
- @Test
- fun dismissButtonClick_nullNotificationKey() {
- player.attach(holder)
- val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(),
- emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null)
- player.bind(state)
-
- verify(keyguardDismissUtil, never())
- .executeWhenUnlocked(
- any(ActivityStarter.OnDismissAction::class.java),
- ArgumentMatchers.anyBoolean()
- )
+ verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
index fdb432c..ab3b208 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt
@@ -16,13 +16,12 @@
package com.android.systemui.media
-import android.app.Notification
import android.graphics.drawable.Drawable
-import android.media.MediaMetadata
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
+import android.media.session.MediaController
+import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSession
-import android.media.session.PlaybackState
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -55,7 +54,6 @@
private const val KEY_OLD = "TEST_KEY_OLD"
private const val PACKAGE = "PKG"
private const val SESSION_KEY = "SESSION_KEY"
-private const val SESSION_ARTIST = "SESSION_ARTIST"
private const val SESSION_TITLE = "SESSION_TITLE"
private const val DEVICE_NAME = "DEVICE_NAME"
private const val USER_ID = 0
@@ -68,6 +66,7 @@
public class MediaDeviceManagerTest : SysuiTestCase() {
private lateinit var manager: MediaDeviceManager
+ @Mock private lateinit var controllerFactory: MediaControllerFactory
@Mock private lateinit var lmmFactory: LocalMediaManagerFactory
@Mock private lateinit var lmm: LocalMediaManager
@Mock private lateinit var mr2: MediaRouter2Manager
@@ -78,10 +77,9 @@
@Mock private lateinit var device: MediaDevice
@Mock private lateinit var icon: Drawable
@Mock private lateinit var route: RoutingSessionInfo
+ @Mock private lateinit var controller: MediaController
+ @Mock private lateinit var playbackInfo: PlaybackInfo
private lateinit var session: MediaSession
- private lateinit var metadataBuilder: MediaMetadata.Builder
- private lateinit var playbackBuilder: PlaybackState.Builder
- private lateinit var notifBuilder: Notification.Builder
private lateinit var mediaData: MediaData
@JvmField @Rule val mockito = MockitoJUnit.rule()
@@ -89,8 +87,8 @@
fun setUp() {
fakeFgExecutor = FakeExecutor(FakeSystemClock())
fakeBgExecutor = FakeExecutor(FakeSystemClock())
- manager = MediaDeviceManager(context, lmmFactory, mr2, fakeFgExecutor, fakeBgExecutor,
- dumpster)
+ manager = MediaDeviceManager(controllerFactory, lmmFactory, mr2, fakeFgExecutor,
+ fakeBgExecutor, dumpster)
manager.addListener(listener)
// Configure mocks.
@@ -101,28 +99,13 @@
whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route)
// Create a media sesssion and notification for testing.
- metadataBuilder = MediaMetadata.Builder().apply {
- putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST)
- putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE)
- }
- playbackBuilder = PlaybackState.Builder().apply {
- setState(PlaybackState.STATE_PAUSED, 6000L, 1f)
- setActions(PlaybackState.ACTION_PLAY)
- }
- session = MediaSession(context, SESSION_KEY).apply {
- setMetadata(metadataBuilder.build())
- setPlaybackState(playbackBuilder.build())
- }
- session.setActive(true)
- notifBuilder = Notification.Builder(context, "NONE").apply {
- setContentTitle(SESSION_TITLE)
- setContentText(SESSION_ARTIST)
- setSmallIcon(android.R.drawable.ic_media_pause)
- setStyle(Notification.MediaStyle().setMediaSession(session.getSessionToken()))
- }
+ session = MediaSession(context, SESSION_KEY)
+
mediaData = MediaData(USER_ID, true, 0, PACKAGE, null, null, SESSION_TITLE, null,
emptyList(), emptyList(), PACKAGE, session.sessionToken, clickIntent = null,
device = null, active = true, resumeAction = null)
+ whenever(controllerFactory.create(session.sessionToken))
+ .thenReturn(controller)
}
@After
@@ -336,6 +319,41 @@
assertThat(data.icon).isNull()
}
+ @Test
+ fun audioInfoChanged() {
+ whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
+ whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
+ // GIVEN a controller with local playback type
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(mr2)
+ // WHEN onAudioInfoChanged fires with remote playback type
+ whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+ verify(controller).registerCallback(captor.capture())
+ captor.value.onAudioInfoChanged(playbackInfo)
+ // THEN the route is checked
+ verify(mr2).getRoutingSessionForMediaController(eq(controller))
+ }
+
+ @Test
+ fun audioInfoHasntChanged() {
+ whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
+ whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
+ // GIVEN a controller with remote playback type
+ manager.onMediaDataLoaded(KEY, null, mediaData)
+ fakeBgExecutor.runAllReady()
+ fakeFgExecutor.runAllReady()
+ reset(mr2)
+ // WHEN onAudioInfoChanged fires with remote playback type
+ val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
+ verify(controller).registerCallback(captor.capture())
+ captor.value.onAudioInfoChanged(playbackInfo)
+ // THEN the route is not checked
+ verify(mr2, never()).getRoutingSessionForMediaController(eq(controller))
+ }
+
fun captureCallback(): LocalMediaManager.DeviceCallback {
val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java)
verify(lmm).registerCallback(captor.capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
new file mode 100644
index 0000000..5d81de6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.SharedPreferences
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import android.graphics.Color
+import android.media.MediaDescription
+import android.media.session.MediaSession
+import android.provider.Settings
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.After
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+private const val KEY = "TEST_KEY"
+private const val OLD_KEY = "RESUME_KEY"
+private const val APP = "APP"
+private const val BG_COLOR = Color.RED
+private const val PACKAGE_NAME = "PKG"
+private const val CLASS_NAME = "CLASS"
+private const val ARTIST = "ARTIST"
+private const val TITLE = "TITLE"
+private const val USER_ID = 0
+private const val MEDIA_PREFERENCES = "media_control_prefs"
+private const val RESUME_COMPONENTS = "package1/class1:package2/class2:package3/class3"
+
+private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+private fun <T> any(): T = Mockito.any<T>()
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class MediaResumeListenerTest : SysuiTestCase() {
+
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var mediaDataManager: MediaDataManager
+ @Mock private lateinit var device: MediaDeviceData
+ @Mock private lateinit var token: MediaSession.Token
+ @Mock private lateinit var tunerService: TunerService
+ @Mock private lateinit var resumeBrowserFactory: ResumeMediaBrowserFactory
+ @Mock private lateinit var resumeBrowser: ResumeMediaBrowser
+ @Mock private lateinit var sharedPrefs: SharedPreferences
+ @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var pendingIntent: PendingIntent
+
+ @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback>
+
+ private lateinit var executor: FakeExecutor
+ private lateinit var data: MediaData
+ private lateinit var resumeListener: MediaResumeListener
+
+ private var originalQsSetting = Settings.Global.getInt(context.contentResolver,
+ Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1)
+ private var originalResumeSetting = Settings.Secure.getInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, 1)
+ Settings.Secure.putInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RESUME, 1)
+
+ whenever(resumeBrowserFactory.create(capture(callbackCaptor), any()))
+ .thenReturn(resumeBrowser)
+
+ // resume components are stored in sharedpreferences
+ whenever(mockContext.getSharedPreferences(eq(MEDIA_PREFERENCES), anyInt()))
+ .thenReturn(sharedPrefs)
+ whenever(sharedPrefs.getString(any(), any())).thenReturn(RESUME_COMPONENTS)
+ whenever(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
+ whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor)
+ whenever(mockContext.packageManager).thenReturn(context.packageManager)
+ whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
+
+ executor = FakeExecutor(FakeSystemClock())
+ resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor,
+ tunerService, resumeBrowserFactory)
+ resumeListener.setManager(mediaDataManager)
+ mediaDataManager.addListener(resumeListener)
+
+ data = MediaData(
+ userId = USER_ID,
+ initialized = true,
+ backgroundColor = BG_COLOR,
+ app = APP,
+ appIcon = null,
+ artist = ARTIST,
+ song = TITLE,
+ artwork = null,
+ actions = emptyList(),
+ actionsToShowInCompact = emptyList(),
+ packageName = PACKAGE_NAME,
+ token = token,
+ clickIntent = null,
+ device = device,
+ active = true,
+ notificationKey = KEY,
+ resumeAction = null)
+ }
+
+ @After
+ fun tearDown() {
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.SHOW_MEDIA_ON_QUICK_SETTINGS, originalQsSetting)
+ Settings.Secure.putInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RESUME, originalResumeSetting)
+ }
+
+ @Test
+ fun testWhenNoResumption_doesNothing() {
+ Settings.Secure.putInt(context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+
+ // When listener is created, we do NOT register a user change listener
+ val listener = MediaResumeListener(context, broadcastDispatcher, executor, tunerService,
+ resumeBrowserFactory)
+ listener.setManager(mediaDataManager)
+ verify(broadcastDispatcher, never()).registerReceiver(eq(listener.userChangeReceiver),
+ any(), any(), any())
+
+ // When data is loaded, we do NOT execute or update anything
+ listener.onMediaDataLoaded(KEY, OLD_KEY, data)
+ assertThat(executor.numPending()).isEqualTo(0)
+ verify(mediaDataManager, never()).setResumeAction(any(), any())
+ }
+
+ @Test
+ fun testOnLoad_checksForResume_noService() {
+ // When media data is loaded that has not been checked yet, and does not have a MBS
+ resumeListener.onMediaDataLoaded(KEY, null, data)
+
+ // Then we report back to the manager
+ verify(mediaDataManager).setResumeAction(KEY, null)
+ }
+
+ @Test
+ fun testOnLoad_checksForResume_hasService() {
+ // Set up mocks to successfully find a MBS that returns valid media
+ val pm = mock(PackageManager::class.java)
+ whenever(mockContext.packageManager).thenReturn(pm)
+ val resolveInfo = ResolveInfo()
+ val serviceInfo = ServiceInfo()
+ serviceInfo.packageName = PACKAGE_NAME
+ resolveInfo.serviceInfo = serviceInfo
+ resolveInfo.serviceInfo.name = CLASS_NAME
+ val resumeInfo = listOf(resolveInfo)
+ whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+
+ val description = MediaDescription.Builder().setTitle(TITLE).build()
+ val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+ whenever(resumeBrowser.testConnection()).thenAnswer {
+ callbackCaptor.value.addTrack(description, component, resumeBrowser)
+ }
+
+ // When media data is loaded that has not been checked yet, and does have a MBS
+ val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
+ resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+
+ // Then we test whether the service is valid
+ executor.runAllReady()
+ verify(resumeBrowser).testConnection()
+
+ // And since it is, we report back to the manager
+ verify(mediaDataManager).setResumeAction(eq(KEY), any())
+
+ // But we do not tell it to add new controls
+ verify(mediaDataManager, never())
+ .addResumptionControls(anyInt(), any(), any(), any(), any(), any(), any())
+
+ // Finally, make sure the resume browser disconnected
+ verify(resumeBrowser).disconnect()
+ }
+
+ @Test
+ fun testOnLoad_doesNotCheckAgain() {
+ // When a media data is loaded that has been checked already
+ var dataCopy = data.copy(hasCheckedForResume = true)
+ resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+
+ // Then we should not check it again
+ verify(resumeBrowser, never()).testConnection()
+ verify(mediaDataManager, never()).setResumeAction(KEY, null)
+ }
+
+ @Test
+ fun testOnUserUnlock_loadsTracks() {
+ // Set up mock service to successfully find valid media
+ val description = MediaDescription.Builder().setTitle(TITLE).build()
+ val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+ whenever(resumeBrowser.token).thenReturn(token)
+ whenever(resumeBrowser.appIntent).thenReturn(pendingIntent)
+ whenever(resumeBrowser.findRecentMedia()).thenAnswer {
+ callbackCaptor.value.addTrack(description, component, resumeBrowser)
+ }
+
+ // Make sure broadcast receiver is registered
+ resumeListener.setManager(mediaDataManager)
+ verify(broadcastDispatcher).registerReceiver(eq(resumeListener.userChangeReceiver),
+ any(), any(), any())
+
+ // When we get an unlock event
+ val intent = Intent(Intent.ACTION_USER_UNLOCKED)
+ resumeListener.userChangeReceiver.onReceive(context, intent)
+
+ // Then we should attempt to find recent media for each saved component
+ verify(resumeBrowser, times(3)).findRecentMedia()
+
+ // Then since the mock service found media, the manager should be informed
+ verify(mediaDataManager, times(3)).addResumptionControls(anyInt(),
+ any(), any(), any(), any(), any(), eq(PACKAGE_NAME))
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index f385243..f397959 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -23,8 +23,9 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -33,7 +34,6 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
@@ -63,10 +63,8 @@
@Mock private lateinit var mediaControllerFactory: MediaControllerFactory
@Mock private lateinit var mediaController: MediaController
- @Mock private lateinit var executor: DelayableExecutor
+ private lateinit var executor: FakeExecutor
@Mock private lateinit var timeoutCallback: (String, Boolean) -> Unit
- @Mock private lateinit var cancellationRunnable: Runnable
- @Captor private lateinit var timeoutCaptor: ArgumentCaptor<Runnable>
@Captor private lateinit var mediaCallbackCaptor: ArgumentCaptor<MediaController.Callback>
@JvmField @Rule val mockito = MockitoJUnit.rule()
private lateinit var metadataBuilder: MediaMetadata.Builder
@@ -78,7 +76,7 @@
@Before
fun setup() {
`when`(mediaControllerFactory.create(any())).thenReturn(mediaController)
- `when`(executor.executeDelayed(any(), anyLong())).thenReturn(cancellationRunnable)
+ executor = FakeExecutor(FakeSystemClock())
mediaTimeoutListener = MediaTimeoutListener(mediaControllerFactory, executor)
mediaTimeoutListener.timeoutCallback = timeoutCallback
@@ -120,7 +118,7 @@
fun testOnMediaDataLoaded_registersTimeout_whenPaused() {
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
- verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+ assertThat(executor.numPending()).isEqualTo(1)
verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
}
@@ -137,6 +135,17 @@
}
@Test
+ fun testOnMediaDataRemoved_clearsTimeout() {
+ // GIVEN media that is paused
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ assertThat(executor.numPending()).isEqualTo(1)
+ // WHEN the media is removed
+ mediaTimeoutListener.onMediaDataRemoved(KEY)
+ // THEN the timeout runnable is cancelled
+ assertThat(executor.numPending()).isEqualTo(0)
+ }
+
+ @Test
fun testOnMediaDataLoaded_migratesKeys() {
// From not playing
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
@@ -151,7 +160,7 @@
verify(mediaController).registerCallback(anyObject())
// Enqueues callback
- verify(executor).execute(anyObject())
+ assertThat(executor.numPending()).isEqualTo(1)
}
@Test
@@ -166,8 +175,9 @@
`when`(mediaController.playbackState).thenReturn(playingState)
mediaTimeoutListener.onMediaDataLoaded("NEWKEY", KEY, mediaData)
- // Never cancels callback, or schedule another one
- verify(cancellationRunnable, never()).run()
+ // The number of queued timeout tasks remains the same. The timeout task isn't cancelled nor
+ // is another scheduled
+ assertThat(executor.numPending()).isEqualTo(1)
}
@Test
@@ -177,7 +187,7 @@
mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
.setState(PlaybackState.STATE_PAUSED, 0L, 0f).build())
- verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+ assertThat(executor.numPending()).isEqualTo(1)
}
@Test
@@ -187,7 +197,7 @@
mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
.setState(PlaybackState.STATE_PLAYING, 0L, 0f).build())
- verify(cancellationRunnable).run()
+ assertThat(executor.numPending()).isEqualTo(0)
}
@Test
@@ -195,10 +205,9 @@
// Assuming we have a pending timeout
testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
- clearInvocations(cancellationRunnable)
mediaCallbackCaptor.value.onPlaybackStateChanged(PlaybackState.Builder()
.setState(PlaybackState.STATE_STOPPED, 0L, 0f).build())
- verify(cancellationRunnable, never()).run()
+ assertThat(executor.numPending()).isEqualTo(1)
}
@Test
@@ -206,7 +215,10 @@
// Assuming we're have a pending timeout
testOnPlaybackStateChanged_schedulesTimeout_whenPaused()
- timeoutCaptor.value.run()
+ with(executor) {
+ advanceClockToNext()
+ runAllReady()
+ }
verify(timeoutCallback).invoke(eq(KEY), eq(true))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
new file mode 100644
index 0000000..d26229e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media
+
+import android.content.ComponentName
+import android.content.Context
+import android.media.MediaDescription
+import android.media.browse.MediaBrowser
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.service.media.MediaBrowserService
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+private const val PACKAGE_NAME = "package"
+private const val CLASS_NAME = "class"
+private const val TITLE = "song title"
+private const val MEDIA_ID = "media ID"
+private const val ROOT = "media browser root"
+
+private fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+private fun <T> any(): T = Mockito.any<T>()
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+public class ResumeMediaBrowserTest : SysuiTestCase() {
+
+ private lateinit var resumeBrowser: TestableResumeMediaBrowser
+ private val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+ private val description = MediaDescription.Builder()
+ .setTitle(TITLE)
+ .setMediaId(MEDIA_ID)
+ .build()
+
+ @Mock lateinit var callback: ResumeMediaBrowser.Callback
+ @Mock lateinit var listener: MediaResumeListener
+ @Mock lateinit var service: MediaBrowserService
+ @Mock lateinit var browserFactory: MediaBrowserFactory
+ @Mock lateinit var browser: MediaBrowser
+ @Mock lateinit var token: MediaSession.Token
+ @Mock lateinit var mediaController: MediaController
+ @Mock lateinit var transportControls: MediaController.TransportControls
+
+ @Captor lateinit var connectionCallback: ArgumentCaptor<MediaBrowser.ConnectionCallback>
+ @Captor lateinit var subscriptionCallback: ArgumentCaptor<MediaBrowser.SubscriptionCallback>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(browserFactory.create(any(), capture(connectionCallback), any()))
+ .thenReturn(browser)
+
+ whenever(mediaController.transportControls).thenReturn(transportControls)
+
+ resumeBrowser = TestableResumeMediaBrowser(context, callback, component, browserFactory,
+ mediaController)
+ }
+
+ @Test
+ fun testConnection_connectionFails_callsOnError() {
+ // When testConnection cannot connect to the service
+ setupBrowserFailed()
+ resumeBrowser.testConnection()
+
+ // Then it calls onError
+ verify(callback).onError()
+ }
+
+ @Test
+ fun testConnection_connects_onConnected() {
+ // When testConnection can connect to the service
+ setupBrowserConnection()
+ resumeBrowser.testConnection()
+
+ // Then it calls onConnected
+ verify(callback).onConnected()
+ }
+
+ @Test
+ fun testConnection_noValidMedia_error() {
+ // When testConnection can connect to the service, and does not find valid media
+ setupBrowserConnectionNoResults()
+ resumeBrowser.testConnection()
+
+ // Then it calls onError
+ verify(callback).onError()
+ }
+
+ @Test
+ fun testConnection_hasValidMedia_addTrack() {
+ // When testConnection can connect to the service, and finds valid media
+ setupBrowserConnectionValidMedia()
+ resumeBrowser.testConnection()
+
+ // Then it calls addTrack
+ verify(callback).onConnected()
+ verify(callback).addTrack(eq(description), eq(component), eq(resumeBrowser))
+ }
+
+ @Test
+ fun testFindRecentMedia_connectionFails_error() {
+ // When findRecentMedia is called and we cannot connect
+ setupBrowserFailed()
+ resumeBrowser.findRecentMedia()
+
+ // Then it calls onError
+ verify(callback).onError()
+ }
+
+ @Test
+ fun testFindRecentMedia_noRoot_error() {
+ // When findRecentMedia is called and does not get a valid root
+ setupBrowserConnection()
+ whenever(browser.getRoot()).thenReturn(null)
+ resumeBrowser.findRecentMedia()
+
+ // Then it calls onError
+ verify(callback).onError()
+ }
+
+ @Test
+ fun testFindRecentMedia_connects_onConnected() {
+ // When findRecentMedia is called and we connect
+ setupBrowserConnection()
+ resumeBrowser.findRecentMedia()
+
+ // Then it calls onConnected
+ verify(callback).onConnected()
+ }
+
+ @Test
+ fun testFindRecentMedia_noChildren_error() {
+ // When findRecentMedia is called and we connect, but do not get any results
+ setupBrowserConnectionNoResults()
+ resumeBrowser.findRecentMedia()
+
+ // Then it calls onError
+ verify(callback).onError()
+ }
+
+ @Test
+ fun testFindRecentMedia_notPlayable_error() {
+ // When findRecentMedia is called and we connect, but do not get a playable child
+ setupBrowserConnectionNotPlayable()
+ resumeBrowser.findRecentMedia()
+
+ // Then it calls onError
+ verify(callback).onError()
+ }
+
+ @Test
+ fun testFindRecentMedia_hasValidMedia_addTrack() {
+ // When findRecentMedia is called and we can connect and get playable media
+ setupBrowserConnectionValidMedia()
+ resumeBrowser.findRecentMedia()
+
+ // Then it calls addTrack
+ verify(callback).addTrack(eq(description), eq(component), eq(resumeBrowser))
+ }
+
+ @Test
+ fun testRestart_connectionFails_error() {
+ // When restart is called and we cannot connect
+ setupBrowserFailed()
+ resumeBrowser.restart()
+
+ // Then it calls onError
+ verify(callback).onError()
+ }
+
+ @Test
+ fun testRestart_connects() {
+ // When restart is called and we connect successfully
+ setupBrowserConnection()
+ resumeBrowser.restart()
+
+ // Then it creates a new controller and sends play command
+ verify(transportControls).prepare()
+ verify(transportControls).play()
+
+ // Then it calls onConnected
+ verify(callback).onConnected()
+ }
+
+ /**
+ * Helper function to mock a failed connection
+ */
+ private fun setupBrowserFailed() {
+ whenever(browser.connect()).thenAnswer {
+ connectionCallback.value.onConnectionFailed()
+ }
+ }
+
+ /**
+ * Helper function to mock a successful connection only
+ */
+ private fun setupBrowserConnection() {
+ whenever(browser.connect()).thenAnswer {
+ connectionCallback.value.onConnected()
+ }
+ whenever(browser.isConnected()).thenReturn(true)
+ whenever(browser.getRoot()).thenReturn(ROOT)
+ whenever(browser.sessionToken).thenReturn(token)
+ }
+
+ /**
+ * Helper function to mock a successful connection, but no media results
+ */
+ private fun setupBrowserConnectionNoResults() {
+ setupBrowserConnection()
+ whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer {
+ subscriptionCallback.value.onChildrenLoaded(ROOT, emptyList())
+ }
+ }
+
+ /**
+ * Helper function to mock a successful connection, but no playable results
+ */
+ private fun setupBrowserConnectionNotPlayable() {
+ setupBrowserConnection()
+
+ val child = MediaBrowser.MediaItem(description, 0)
+
+ whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer {
+ subscriptionCallback.value.onChildrenLoaded(ROOT, listOf(child))
+ }
+ }
+
+ /**
+ * Helper function to mock a successful connection with playable media
+ */
+ private fun setupBrowserConnectionValidMedia() {
+ setupBrowserConnection()
+
+ val child = MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_PLAYABLE)
+
+ whenever(browser.serviceComponent).thenReturn(component)
+ whenever(browser.subscribe(any(), capture(subscriptionCallback))).thenAnswer {
+ subscriptionCallback.value.onChildrenLoaded(ROOT, listOf(child))
+ }
+ }
+
+ /**
+ * Override so media controller use is testable
+ */
+ private class TestableResumeMediaBrowser(
+ context: Context,
+ callback: Callback,
+ componentName: ComponentName,
+ browserFactory: MediaBrowserFactory,
+ private val fakeController: MediaController
+ ) : ResumeMediaBrowser(context, callback, componentName, browserFactory) {
+
+ override fun createMediaController(token: MediaSession.Token): MediaController {
+ return fakeController
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 42b21c6..27b5b7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -36,7 +36,6 @@
import androidx.test.filters.SmallTest;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
@@ -55,7 +54,6 @@
// Mock
private MediaOutputBaseAdapter mMediaOutputBaseAdapter = mock(MediaOutputBaseAdapter.class);
-
private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
private ShadeController mShadeController = mock(ShadeController.class);
@@ -157,30 +155,6 @@
verify(mMediaOutputBaseAdapter).notifyDataSetChanged();
}
- @Test
- public void refresh_with6Devices_checkBottomPaddingVisibility() {
- for (int i = 0; i < 6; i++) {
- mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class));
- }
- mMediaOutputBaseDialogImpl.refresh();
- final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
- R.id.list_bottom_padding);
-
- assertThat(view.getVisibility()).isEqualTo(View.GONE);
- }
-
- @Test
- public void refresh_with5Devices_checkBottomPaddingVisibility() {
- for (int i = 0; i < 5; i++) {
- mMediaOutputController.mMediaDevices.add(mock(MediaDevice.class));
- }
- mMediaOutputBaseDialogImpl.refresh();
- final View view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById(
- R.id.list_bottom_padding);
-
- assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
- }
-
class MediaOutputBaseDialogImpl extends MediaOutputBaseDialog {
MediaOutputBaseDialogImpl(Context context, MediaOutputController mediaOutputController) {
@@ -189,24 +163,34 @@
mAdapter = mMediaOutputBaseAdapter;
}
+ @Override
int getHeaderIconRes() {
return mHeaderIconRes;
}
+ @Override
IconCompat getHeaderIcon() {
return mIconCompat;
}
+ @Override
int getHeaderIconSize() {
return 10;
}
+ @Override
CharSequence getHeaderText() {
return mHeaderTitle;
}
+ @Override
CharSequence getHeaderSubtitle() {
return mHeaderSubtitle;
}
+
+ @Override
+ int getStopButtonVisibility() {
+ return 0;
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
new file mode 100644
index 0000000..ca328fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.dialog;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.media.MediaRoute2Info;
+import android.media.session.MediaSessionManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.media.LocalMediaManager;
+import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.phone.ShadeController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class MediaOutputDialogTest extends SysuiTestCase {
+
+ private static final String TEST_PACKAGE = "test_package";
+
+ // Mock
+ private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class);
+ private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class);
+ private ShadeController mShadeController = mock(ShadeController.class);
+ private ActivityStarter mStarter = mock(ActivityStarter.class);
+ private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class);
+ private MediaDevice mMediaDevice = mock(MediaDevice.class);
+
+ private MediaOutputDialog mMediaOutputDialog;
+ private MediaOutputController mMediaOutputController;
+ private List<String> mFeatures = new ArrayList<>();
+
+ @Before
+ public void setUp() {
+ mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
+ mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter);
+ mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
+ mMediaOutputDialog = new MediaOutputDialog(mContext, false, mMediaOutputController);
+
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
+ when(mMediaDevice.getFeatures()).thenReturn(mFeatures);
+ }
+
+ @Test
+ public void getStopButtonVisibility_remoteDevice_returnVisible() {
+ mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+
+ mFeatures.clear();
+ mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+
+ mFeatures.clear();
+ mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+
+ mFeatures.clear();
+ mFeatures.add(MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void getStopButtonVisibility_localDevice_returnGone() {
+ mFeatures.add(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK);
+
+ assertThat(mMediaOutputDialog.getStopButtonVisibility()).isEqualTo(View.GONE);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
index 6f5cbe2..89ca32c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/PipAnimationControllerTest.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.graphics.Matrix;
@@ -34,7 +33,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.ConfigurationController;
import org.junit.Before;
import org.junit.Test;
@@ -61,7 +59,7 @@
@Before
public void setUp() throws Exception {
mPipAnimationController = new PipAnimationController(
- new PipSurfaceTransactionHelper(mContext, mock(ConfigurationController.class)));
+ new PipSurfaceTransactionHelper(mContext));
mLeash = new SurfaceControl.Builder()
.setContainerLayer()
.setName("FakeLeash")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipControllerTest.java
index b043495..1274621 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipControllerTest.java
@@ -32,16 +32,11 @@
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipBoundsHandler;
-import com.android.systemui.pip.PipSurfaceTransactionHelper;
import com.android.systemui.pip.PipTaskOrganizer;
-import com.android.systemui.pip.PipUiEventLogger;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.systemui.wmshell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayController;
import org.junit.Before;
@@ -61,17 +56,16 @@
private TestableContext mSpiedContext;
@Mock private ActivityManagerWrapper mMockActivityManagerWrapper;
- @Mock private BroadcastDispatcher mMockBroadcastDispatcher;
@Mock private ConfigurationController mMockConfigurationController;
- @Mock private DeviceConfigProxy mMockDeviceConfigProxy;
@Mock private DisplayController mMockdDisplayController;
- @Mock private FloatingContentCoordinator mMockFloatingContentCoordinator;
@Mock private PackageManager mPackageManager;
+ @Mock private PipMenuActivityController mMockPipMenuActivityController;
+ @Mock private PipAppOpsListener mMockPipAppOpsListener;
@Mock private PipBoundsHandler mMockPipBoundsHandler;
- @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
+ @Mock private PipMediaController mMockPipMediaController;
@Mock private PipTaskOrganizer mMockPipTaskOrganizer;
- @Mock private PipUiEventLogger mPipUiEventLogger;
- @Mock private SysUiState mMockSysUiState;
+ @Mock private PipTouchHandler mMockPipTouchHandler;
+ @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
@Before
public void setUp() throws RemoteException {
@@ -82,10 +76,10 @@
when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
- mPipController = new PipController(mSpiedContext, mMockBroadcastDispatcher,
- mMockConfigurationController, mMockDeviceConfigProxy, mMockdDisplayController,
- mMockFloatingContentCoordinator, mMockSysUiState, mMockPipBoundsHandler,
- mMockPipSurfaceTransactionHelper, mMockPipTaskOrganizer, mPipUiEventLogger);
+ mPipController = new PipController(mSpiedContext, mMockdDisplayController,
+ mMockPipAppOpsListener,
+ mMockPipBoundsHandler, mMockPipMediaController, mMockPipMenuActivityController,
+ mMockPipTaskOrganizer, mMockPipTouchHandler, mMockWindowManagerShellWrapper);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
index c8d4aca..ad83ebb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchHandlerTest.java
@@ -22,7 +22,6 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.app.IActivityManager;
import android.graphics.Point;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -33,13 +32,10 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.model.SysUiState;
import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.pip.PipTaskOrganizer;
import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.FloatingContentCoordinator;
import org.junit.Before;
@@ -63,27 +59,15 @@
private PipTouchHandler mPipTouchHandler;
@Mock
- private IActivityManager mActivityManager;
-
- @Mock
private PipMenuActivityController mPipMenuActivityController;
@Mock
- private InputConsumerController mInputConsumerController;
-
- @Mock
private PipTaskOrganizer mPipTaskOrganizer;
@Mock
private FloatingContentCoordinator mFloatingContentCoordinator;
@Mock
- private DeviceConfigProxy mDeviceConfigProxy;
-
- @Mock
- private SysUiState mSysUiState;
-
- @Mock
private PipUiEventLogger mPipUiEventLogger;
private PipBoundsHandler mPipBoundsHandler;
@@ -105,9 +89,8 @@
mPipBoundsHandler = new PipBoundsHandler(mContext);
mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm();
mPipSnapAlgorithm = new PipSnapAlgorithm(mContext);
- mPipTouchHandler = new PipTouchHandler(mContext, mActivityManager,
- mPipMenuActivityController, mInputConsumerController, mPipBoundsHandler,
- mPipTaskOrganizer, mFloatingContentCoordinator, mDeviceConfigProxy, mSysUiState,
+ mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController,
+ mPipBoundsHandler, mPipTaskOrganizer, mFloatingContentCoordinator,
mPipUiEventLogger);
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
mPipResizeGestureHandler = Mockito.spy(mPipTouchHandler.getPipResizeGestureHandler());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 5d14898..faf43a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -43,6 +43,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
+import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.NetworkController;
@@ -73,12 +74,16 @@
@Mock
private QSTileHost mHost;
@Mock
- NetworkController.SignalCallback mCallback;
+ NetworkController.SignalCallback mSignalCallback;
@Mock
private MetricsLogger mMetricsLogger;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
+ private HotspotController mHotspotController;
+ @Mock
+ private HotspotController.Callback mHotspotCallback;
+ @Mock
private QSLogger mQSLogger;
private TestableLooper mTestableLooper;
@@ -101,7 +106,8 @@
mQSLogger,
mController,
mKeyguard,
- mNetworkController
+ mNetworkController,
+ mHotspotController
);
// We are not setting the mocks to listening, so we trigger a first refresh state to
@@ -113,14 +119,22 @@
ArgumentCaptor.forClass(NetworkController.SignalCallback.class);
verify(mNetworkController).observe(any(LifecycleOwner.class),
signalCallbackArgumentCaptor.capture());
- mCallback = signalCallbackArgumentCaptor.getValue();
+ mSignalCallback = signalCallbackArgumentCaptor.getValue();
+
+ ArgumentCaptor<HotspotController.Callback> hotspotCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(HotspotController.Callback.class);
+ verify(mHotspotController).observe(any(LifecycleOwner.class),
+ hotspotCallbackArgumentCaptor.capture());
+ mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
}
+ // -------------------------------------------------
+ // All these tests for enabled/disabled wifi have hotspot not enabled
@Test
public void testStateUnavailable_wifiDisabled() {
NetworkController.IconState qsIcon =
new NetworkController.IconState(false, 0, "");
- mCallback.setWifiIndicators(false, mock(NetworkController.IconState.class),
+ mSignalCallback.setWifiIndicators(false, mock(NetworkController.IconState.class),
qsIcon, false,false, "",
false, "");
mTestableLooper.processAllMessages();
@@ -132,7 +146,7 @@
public void testStateUnavailable_wifiNotConnected() {
NetworkController.IconState qsIcon =
new NetworkController.IconState(false, 0, "");
- mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class),
+ mSignalCallback.setWifiIndicators(true, mock(NetworkController.IconState.class),
qsIcon, false,false, "",
false, "");
mTestableLooper.processAllMessages();
@@ -143,7 +157,7 @@
private void enableWifiAndProcessMessages() {
NetworkController.IconState qsIcon =
new NetworkController.IconState(true, 0, "");
- mCallback.setWifiIndicators(true, mock(NetworkController.IconState.class),
+ mSignalCallback.setWifiIndicators(true, mock(NetworkController.IconState.class),
qsIcon, false,false, "",
false, "");
mTestableLooper.processAllMessages();
@@ -166,6 +180,46 @@
enableWifiAndProcessMessages();
assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
}
+ // -------------------------------------------------
+
+ // -------------------------------------------------
+ // All these tests for enabled/disabled hotspot have wifi not enabled
+ @Test
+ public void testStateUnavailable_hotspotDisabled() {
+ mHotspotCallback.onHotspotChanged(false, 0);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void testStateUnavailable_hotspotEnabledNotConnected() {
+ mHotspotCallback.onHotspotChanged(true, 0);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void testStateActive_hotspotEnabledAndConnectedAndCasting() {
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastController.CastDevice.STATE_CONNECTED;
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ mHotspotCallback.onHotspotChanged(true, 1);
+ mTestableLooper.processAllMessages();
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void testStateInactive_hotspotEnabledAndConnectedAndNotCasting() {
+ mHotspotCallback.onHotspotChanged(true, 1);
+ mTestableLooper.processAllMessages();
+ assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
+ }
+ // -------------------------------------------------
@Test
public void testHandleClick_castDevicePresent() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 402a99d..dee6020 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -108,11 +108,7 @@
return new TestableAlertingNotificationManager();
}
- protected StatusBarNotification createNewNotification(int id) {
- Notification.Builder n = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text");
+ protected StatusBarNotification createNewSbn(int id, Notification.Builder n) {
return new StatusBarNotification(
TEST_PACKAGE_NAME /* pkg */,
TEST_PACKAGE_NAME,
@@ -126,6 +122,14 @@
0 /* postTime */);
}
+ protected StatusBarNotification createNewNotification(int id) {
+ Notification.Builder n = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+ return createNewSbn(id, n);
+ }
+
@Before
public void setUp() {
mTestHandler = Handler.createAsync(Looper.myLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index a36a4c4..0bf1ac3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -45,6 +45,7 @@
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doThrow
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -79,6 +80,7 @@
@Before
fun setup() {
`when`(root.viewRootImpl).thenReturn(viewRootImpl)
+ `when`(root.isAttachedToWindow).thenReturn(true)
`when`(statusBarStateController.state).then { statusBarState }
`when`(blurUtils.blurRadiusOfRatio(anyFloat())).then { answer ->
(answer.arguments[0] as Float * maxBlur).toInt()
@@ -219,6 +221,13 @@
@Test
fun updateBlurCallback_invalidWindow() {
+ `when`(root.isAttachedToWindow).thenReturn(false)
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+ verify(wallpaperManager, times(0)).setWallpaperZoomOut(any(), anyFloat())
+ }
+
+ @Test
+ fun updateBlurCallback_exception() {
doThrow(IllegalArgumentException("test exception")).`when`(wallpaperManager)
.setWallpaperZoomOut(any(), anyFloat())
notificationShadeDepthController.updateBlurCallback.doFrame(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index fc7d0ce..0e4b053 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -16,12 +16,15 @@
package com.android.systemui.statusbar.policy;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
+import android.app.Notification;
import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -30,6 +33,7 @@
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import org.junit.Before;
import org.junit.Test;
@@ -84,5 +88,25 @@
assertTrue("Heads up should live long enough", mLivesPastNormalTime);
assertFalse(mHeadsUpManager.isAlerting(mEntry.getKey()));
}
+
+ @Test
+ public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
+ HeadsUpManager.HeadsUpEntry ongoingCall = mHeadsUpManager.new HeadsUpEntry();
+ ongoingCall.setEntry(new NotificationEntryBuilder()
+ .setSbn(createNewSbn(0,
+ new Notification.Builder(mContext, "")
+ .setCategory(Notification.CATEGORY_CALL)
+ .setOngoing(true)))
+ .build());
+
+ HeadsUpManager.HeadsUpEntry activeRemoteInput = mHeadsUpManager.new HeadsUpEntry();
+ activeRemoteInput.setEntry(new NotificationEntryBuilder()
+ .setSbn(createNewNotification(1))
+ .build());
+ activeRemoteInput.remoteInputActive = true;
+
+ assertThat(ongoingCall.compareTo(activeRemoteInput)).isLessThan(0);
+ assertThat(activeRemoteInput.compareTo(ongoingCall)).isGreaterThan(0);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
index 280423f..0b75950 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -37,9 +37,12 @@
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.pip.Pip;
+import com.android.systemui.pip.phone.PipTouchHandler;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
@@ -59,9 +62,11 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class WMShellTest extends SysuiTestCase {
-
+ InputConsumerController mInputConsumerController;
WMShell mWMShell;
+
@Mock CommandQueue mCommandQueue;
+ @Mock ConfigurationController mConfigurationController;
@Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock ActivityManagerWrapper mActivityManagerWrapper;
@Mock DisplayImeController mDisplayImeController;
@@ -69,6 +74,7 @@
@Mock ScreenLifecycle mScreenLifecycle;
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
+ @Mock PipTouchHandler mPipTouchHandler;
@Mock SplitScreen mSplitScreen;
@Mock OneHanded mOneHanded;
@Mock ShellTaskOrganizer mTaskOrganizer;
@@ -78,10 +84,16 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mWMShell = new WMShell(mContext, mCommandQueue, mKeyguardUpdateMonitor,
- mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
- mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
- Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
+ mInputConsumerController = InputConsumerController.getPipInputConsumer();
+
+ mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
+ mInputConsumerController, mKeyguardUpdateMonitor, mActivityManagerWrapper,
+ mDisplayImeController, mNavigationModeController, mScreenLifecycle, mSysUiState,
+ Optional.of(mPip), Optional.of(mSplitScreen), Optional.of(mOneHanded),
+ mTaskOrganizer, mProtoTracer);
+
+ when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
+
}
@Test
@@ -103,9 +115,8 @@
TestableContext spiedContext = spy(mContext);
when(mMockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(spiedContext.getPackageManager()).thenReturn(mMockPackageManager);
-
final WMShell nonPipWMShell = new WMShell(spiedContext, mCommandQueue,
- mKeyguardUpdateMonitor,
+ mConfigurationController, mInputConsumerController, mKeyguardUpdateMonitor,
mActivityManagerWrapper, mDisplayImeController, mNavigationModeController,
mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen),
Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer);
diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
index f6eb40a..94c871d 100644
--- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
+++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
@@ -17,18 +17,63 @@
#include <errno.h>
#include <error.h>
#include <jni.h>
+#include <linux/filter.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/JNIHelpCompat.h>
#include <nativehelper/ScopedUtfChars.h>
#include <net/if.h>
+#include <netinet/ether.h>
+#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <sys/socket.h>
+#include <stdio.h>
#define LOG_TAG "TetheringUtils"
#include <android/log.h>
namespace android {
+static const uint32_t kIPv6NextHeaderOffset = offsetof(ip6_hdr, ip6_nxt);
+static const uint32_t kIPv6PayloadStart = sizeof(ip6_hdr);
+static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
+
+static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) {
+ sock_filter filter_code[] = {
+ // Check header is ICMPv6.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
+
+ // Check ICMPv6 type.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, type, 0, 1),
+
+ // Accept or reject.
+ BPF_STMT(BPF_RET | BPF_K, 0xffff),
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+
+ const sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
+static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+ android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
+}
+
+static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+ android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
+}
+
static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
jint ifIndex)
{
@@ -125,7 +170,12 @@
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
+ { "setupNaSocket", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_net_util_setupNaSocket },
+ { "setupNsSocket", "(Ljava/io/FileDescriptor;)V",
+ (void*) android_net_util_setupNsSocket },
+ { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V",
+ (void*) android_net_util_setupRaSocket },
};
int register_android_net_util_TetheringUtils(JNIEnv* env) {
diff --git a/packages/Tethering/src/android/net/ip/DadProxy.java b/packages/Tethering/src/android/net/ip/DadProxy.java
new file mode 100644
index 0000000..e2976b7
--- /dev/null
+++ b/packages/Tethering/src/android/net/ip/DadProxy.java
@@ -0,0 +1,54 @@
+/*
+ * 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.net.ip;
+
+import android.net.util.InterfaceParams;
+import android.os.Handler;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Basic Duplicate address detection proxy.
+ *
+ * @hide
+ */
+public class DadProxy {
+ private static final String TAG = DadProxy.class.getSimpleName();
+
+ @VisibleForTesting
+ public static NeighborPacketForwarder naForwarder;
+ public static NeighborPacketForwarder nsForwarder;
+
+ public DadProxy(Handler h, InterfaceParams tetheredIface) {
+ naForwarder = new NeighborPacketForwarder(h, tetheredIface,
+ NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
+ nsForwarder = new NeighborPacketForwarder(h, tetheredIface,
+ NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
+ }
+
+ /** Stop NS/NA Forwarders. */
+ public void stop() {
+ naForwarder.stop();
+ nsForwarder.stop();
+ }
+
+ /** Set upstream iface on both forwarders. */
+ public void setUpstreamIface(InterfaceParams upstreamIface) {
+ naForwarder.setUpstreamIface(upstreamIface);
+ nsForwarder.setUpstreamIface(upstreamIface);
+ }
+}
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 673cbf0..336124d 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -51,6 +51,7 @@
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -160,6 +161,15 @@
/** Capture IpServer dependencies, for injection. */
public abstract static class Dependencies {
+ /**
+ * Create a DadProxy instance to be used by IpServer.
+ * To support multiple tethered interfaces concurrently DAD Proxy
+ * needs to be supported per IpServer instead of per upstream.
+ */
+ public DadProxy getDadProxy(Handler handler, InterfaceParams ifParams) {
+ return new DadProxy(handler, ifParams);
+ }
+
/** Create an IpNeighborMonitor to be used by this IpServer */
public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log,
IpNeighborMonitor.NeighborEventConsumer consumer) {
@@ -256,6 +266,7 @@
// Advertisements (otherwise, we do not add them to mLinkProperties at all).
private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
+ private DadProxy mDadProxy;
// To be accessed only on the handler thread
private int mDhcpServerStartIndex = 0;
@@ -674,6 +685,13 @@
return false;
}
+ // TODO: use ShimUtils instead of explicitly checking the version here.
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME)
+ || "T".equals(Build.VERSION.CODENAME)) {
+ // DAD Proxy starts forwarding packets after IPv6 upstream is present.
+ mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams);
+ }
+
return true;
}
@@ -685,6 +703,11 @@
mRaDaemon.stop();
mRaDaemon = null;
}
+
+ if (mDadProxy != null) {
+ mDadProxy.stop();
+ mDadProxy = null;
+ }
}
// IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
@@ -702,11 +725,16 @@
}
RaParams params = null;
- int upstreamIfindex = 0;
+ String upstreamIface = null;
+ InterfaceParams upstreamIfaceParams = null;
+ int upstreamIfIndex = 0;
if (v6only != null) {
- final String upstreamIface = v6only.getInterfaceName();
-
+ upstreamIface = v6only.getInterfaceName();
+ upstreamIfaceParams = mDeps.getInterfaceParams(upstreamIface);
+ if (upstreamIfaceParams != null) {
+ upstreamIfIndex = upstreamIfaceParams.index;
+ }
params = new RaParams();
params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
@@ -726,15 +754,13 @@
}
}
- upstreamIfindex = mDeps.getIfindex(upstreamIface);
-
// Add upstream index to name mapping for the tether stats usage in the coordinator.
// Although this mapping could be added by both class Tethering and IpServer, adding
// mapping from IpServer guarantees that the mapping is added before the adding
// forwarding rules. That is because there are different state machines in both
// classes. It is hard to guarantee the link property update order between multiple
// state machines.
- mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfindex, upstreamIface);
+ mBpfCoordinator.addUpstreamNameToLookupTable(upstreamIfIndex, upstreamIface);
}
// If v6only is null, we pass in null to setRaParams(), which handles
@@ -743,8 +769,11 @@
setRaParams(params);
mLastIPv6LinkProperties = v6only;
- updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null);
- mLastIPv6UpstreamIfindex = upstreamIfindex;
+ updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfIndex, null);
+ mLastIPv6UpstreamIfindex = upstreamIfIndex;
+ if (mDadProxy != null) {
+ mDadProxy.setUpstreamIface(upstreamIfaceParams);
+ }
}
private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
new file mode 100644
index 0000000..73fc833
--- /dev/null
+++ b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
@@ -0,0 +1,180 @@
+/*
+ * 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.net.ip;
+
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_PACKET;
+import static android.system.OsConstants.ETH_P_IPV6;
+import static android.system.OsConstants.IPPROTO_RAW;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_NONBLOCK;
+import static android.system.OsConstants.SOCK_RAW;
+
+import android.net.util.InterfaceParams;
+import android.net.util.PacketReader;
+import android.net.util.SocketUtils;
+import android.net.util.TetheringUtils;
+import android.os.Handler;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+/**
+ * Basic IPv6 Neighbor Advertisement Forwarder.
+ *
+ * Forward NA packets from upstream iface to tethered iface
+ * and NS packets from tethered iface to upstream iface.
+ *
+ * @hide
+ */
+public class NeighborPacketForwarder extends PacketReader {
+ private final String mTag;
+
+ private FileDescriptor mFd;
+
+ // TODO: get these from NetworkStackConstants.
+ private static final int IPV6_ADDR_LEN = 16;
+ private static final int IPV6_DST_ADDR_OFFSET = 24;
+ private static final int IPV6_HEADER_LEN = 40;
+ private static final int ETH_HEADER_LEN = 14;
+
+ private InterfaceParams mListenIfaceParams, mSendIfaceParams;
+
+ private final int mType;
+ public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136;
+ public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135;
+
+ public NeighborPacketForwarder(Handler h, InterfaceParams tetheredInterface, int type) {
+ super(h);
+ mTag = NeighborPacketForwarder.class.getSimpleName() + "-"
+ + tetheredInterface.name + "-" + type;
+ mType = type;
+
+ if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
+ mSendIfaceParams = tetheredInterface;
+ } else {
+ mListenIfaceParams = tetheredInterface;
+ }
+ }
+
+ /** Set new upstream iface and start/stop based on new params. */
+ public void setUpstreamIface(InterfaceParams upstreamParams) {
+ final InterfaceParams oldUpstreamParams;
+
+ if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
+ oldUpstreamParams = mListenIfaceParams;
+ mListenIfaceParams = upstreamParams;
+ } else {
+ oldUpstreamParams = mSendIfaceParams;
+ mSendIfaceParams = upstreamParams;
+ }
+
+ if (oldUpstreamParams == null && upstreamParams != null) {
+ start();
+ } else if (oldUpstreamParams != null && upstreamParams == null) {
+ stop();
+ } else if (oldUpstreamParams != null && upstreamParams != null
+ && oldUpstreamParams.index != upstreamParams.index) {
+ stop();
+ start();
+ }
+ }
+
+ // TODO: move NetworkStackUtils.closeSocketQuietly to
+ // frameworks/libs/net/common/device/com/android/net/module/util/[someclass].
+ private void closeSocketQuietly(FileDescriptor fd) {
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException ignored) {
+ }
+ }
+
+ @Override
+ protected FileDescriptor createFd() {
+ try {
+ // ICMPv6 packets from modem do not have eth header, so RAW socket cannot be used.
+ // To keep uniformity in both directions PACKET socket can be used.
+ mFd = Os.socket(AF_PACKET, SOCK_DGRAM | SOCK_NONBLOCK, 0);
+
+ // TODO: convert setup*Socket to setupICMPv6BpfFilter with filter type?
+ if (mType == ICMPV6_NEIGHBOR_ADVERTISEMENT) {
+ TetheringUtils.setupNaSocket(mFd);
+ } else if (mType == ICMPV6_NEIGHBOR_SOLICITATION) {
+ TetheringUtils.setupNsSocket(mFd);
+ }
+
+ SocketAddress bindAddress = SocketUtils.makePacketSocketAddress(
+ ETH_P_IPV6, mListenIfaceParams.index);
+ Os.bind(mFd, bindAddress);
+ } catch (ErrnoException | SocketException e) {
+ Log.wtf(mTag, "Failed to create socket", e);
+ closeSocketQuietly(mFd);
+ return null;
+ }
+
+ return mFd;
+ }
+
+ private Inet6Address getIpv6DestinationAddress(byte[] recvbuf) {
+ Inet6Address dstAddr;
+ try {
+ dstAddr = (Inet6Address) Inet6Address.getByAddress(Arrays.copyOfRange(recvbuf,
+ IPV6_DST_ADDR_OFFSET, IPV6_DST_ADDR_OFFSET + IPV6_ADDR_LEN));
+ } catch (UnknownHostException | ClassCastException impossible) {
+ throw new AssertionError("16-byte array not valid IPv6 address?");
+ }
+ return dstAddr;
+ }
+
+ @Override
+ protected void handlePacket(byte[] recvbuf, int length) {
+ if (mSendIfaceParams == null) {
+ return;
+ }
+
+ // The BPF filter should already have checked the length of the packet, but...
+ if (length < IPV6_HEADER_LEN) {
+ return;
+ }
+ Inet6Address destv6 = getIpv6DestinationAddress(recvbuf);
+ if (!destv6.isMulticastAddress()) {
+ return;
+ }
+ InetSocketAddress dest = new InetSocketAddress(destv6, 0);
+
+ FileDescriptor fd = null;
+ try {
+ fd = Os.socket(AF_INET6, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_RAW);
+ SocketUtils.bindSocketToInterface(fd, mSendIfaceParams.name);
+
+ int ret = Os.sendto(fd, recvbuf, 0, length, 0, dest);
+ } catch (ErrnoException | SocketException e) {
+ Log.e(mTag, "handlePacket error: " + e);
+ } finally {
+ closeSocketQuietly(fd);
+ }
+ }
+}
diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 6f017dc..7c0b7cc7 100644
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -18,6 +18,7 @@
import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+import static android.net.util.TetheringUtils.getAllNodesForScopeId;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static android.system.OsConstants.SOCK_RAW;
@@ -44,7 +45,6 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
-import java.net.UnknownHostException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -92,10 +92,6 @@
private static final int DAY_IN_SECONDS = 86_400;
- private static final byte[] ALL_NODES = new byte[] {
- (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
- };
-
private final InterfaceParams mInterface;
private final InetSocketAddress mAllNodes;
@@ -240,7 +236,6 @@
}
}
-
public RouterAdvertisementDaemon(InterfaceParams ifParams) {
mInterface = ifParams;
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mInterface.index), 0);
@@ -363,15 +358,6 @@
}
}
- private static Inet6Address getAllNodesForScopeId(int scopeId) {
- try {
- return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
- } catch (UnknownHostException uhe) {
- Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe);
- return null;
- }
- }
-
private static byte asByte(int value) {
return (byte) value;
}
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
index b17b4ba..53b54f7 100644
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -17,11 +17,15 @@
import android.net.TetherStatsParcel;
import android.net.TetheringRequestParcel;
+import android.util.Log;
import androidx.annotation.NonNull;
import java.io.FileDescriptor;
+import java.net.Inet6Address;
import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -30,6 +34,24 @@
* {@hide}
*/
public class TetheringUtils {
+ public static final byte[] ALL_NODES = new byte[] {
+ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
+ };
+
+ /**
+ * Configures a socket for receiving and sending ICMPv6 neighbor advertisments.
+ * @param fd the socket's {@link FileDescriptor}.
+ */
+ public static native void setupNaSocket(FileDescriptor fd)
+ throws SocketException;
+
+ /**
+ * Configures a socket for receiving and sending ICMPv6 neighbor solicitations.
+ * @param fd the socket's {@link FileDescriptor}.
+ */
+ public static native void setupNsSocket(FileDescriptor fd)
+ throws SocketException;
+
/**
* The object which records offload Tx/Rx forwarded bytes/packets.
* TODO: Replace the inner class ForwardedStats of class OffloadHardwareInterface with
@@ -129,4 +151,15 @@
&& request.exemptFromEntitlementCheck == otherRequest.exemptFromEntitlementCheck
&& request.showProvisioningUi == otherRequest.showProvisioningUi;
}
+
+ /** Get inet6 address for all nodes given scope ID. */
+ public static Inet6Address getAllNodesForScopeId(int scopeId) {
+ try {
+ return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
+ } catch (UnknownHostException uhe) {
+ Log.wtf("TetheringUtils", "Failed to construct Inet6Address from "
+ + Arrays.toString(ALL_NODES) + " and scopedId " + scopeId);
+ return null;
+ }
+ }
}
diff --git a/packages/Tethering/tests/integration/Android.bp b/packages/Tethering/tests/integration/Android.bp
index ed69b7d..02bab9b 100644
--- a/packages/Tethering/tests/integration/Android.bp
+++ b/packages/Tethering/tests/integration/Android.bp
@@ -22,7 +22,6 @@
static_libs: [
"NetworkStackApiStableLib",
"androidx.test.rules",
- "frameworks-base-testutils",
"mockito-target-extended-minus-junit4",
"net-tests-utils",
"testables",
diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp
index a0fb246..9217345 100644
--- a/packages/Tethering/tests/privileged/Android.bp
+++ b/packages/Tethering/tests/privileged/Android.bp
@@ -14,8 +14,22 @@
// limitations under the License.
//
+java_defaults {
+ name: "TetheringPrivilegedTestsJniDefaults",
+ jni_libs: [
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ "libtetherutilsjni",
+ ],
+ jni_uses_sdk_apis: true,
+ visibility: ["//visibility:private"],
+}
+
android_test {
name: "TetheringPrivilegedTests",
+ defaults: [
+ "TetheringPrivilegedTestsJniDefaults",
+ ],
srcs: [
"src/**/*.java",
"src/**/*.kt",
@@ -23,8 +37,13 @@
certificate: "networkstack",
platform_apis: true,
test_suites: [
- "general-tests",
+ "device-tests",
"mts",
],
+ static_libs: [
+ "androidx.test.rules",
+ "net-tests-utils",
+ "TetheringApiCurrentLib",
+ ],
compile_multilib: "both",
}
diff --git a/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
new file mode 100644
index 0000000..747d3e8
--- /dev/null
+++ b/packages/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -0,0 +1,338 @@
+/*
+ * 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.net.ip;
+
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
+
+import static com.android.internal.util.BitUtils.uint16;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.INetd;
+import android.net.InetAddresses;
+import android.net.MacAddress;
+import android.net.TestNetworkInterface;
+import android.net.TestNetworkManager;
+import android.net.util.InterfaceParams;
+import android.net.util.IpUtils;
+import android.net.util.TetheringUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.system.ErrnoException;
+import android.system.Os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.TapPacketReader;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DadProxyTest {
+ private static final int DATA_BUFFER_LEN = 4096;
+ private static final int PACKET_TIMEOUT_MS = 5_000;
+
+ // TODO: make NetworkStackConstants accessible to this test and use the constant from there.
+ private static final int ETHER_SRC_ADDR_OFFSET = 6;
+
+ private DadProxy mProxy;
+ TestNetworkInterface mUpstreamTestIface, mTetheredTestIface;
+ private InterfaceParams mUpstreamParams, mTetheredParams;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private TapPacketReader mUpstreamPacketReader, mTetheredPacketReader;
+ private FileDescriptor mUpstreamTapFd, mTetheredTapFd;
+
+ private static INetd sNetd;
+
+ @BeforeClass
+ public static void setupOnce() {
+ System.loadLibrary("tetherutilsjni");
+
+ final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ final IBinder netdIBinder =
+ (IBinder) inst.getContext().getSystemService(Context.NETD_SERVICE);
+ sNetd = INetd.Stub.asInterface(netdIBinder);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mHandlerThread = new HandlerThread(getClass().getSimpleName());
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+
+ setupTapInterfaces();
+
+ // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads.
+ if (Looper.myLooper() == null) Looper.prepare();
+
+ DadProxy mProxy = setupProxy();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mHandlerThread != null) {
+ mHandler.post(mUpstreamPacketReader::stop); // Also closes the socket
+ mHandler.post(mTetheredPacketReader::stop); // Also closes the socket
+ mUpstreamTapFd = null;
+ mTetheredTapFd = null;
+ mHandlerThread.quitSafely();
+ }
+
+ if (mTetheredParams != null) {
+ sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
+ }
+ if (mUpstreamParams != null) {
+ sNetd.networkRemoveInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
+ }
+
+ if (mUpstreamTestIface != null) {
+ try {
+ Os.close(mUpstreamTestIface.getFileDescriptor().getFileDescriptor());
+ } catch (ErrnoException e) { }
+ }
+
+ if (mTetheredTestIface != null) {
+ try {
+ Os.close(mTetheredTestIface.getFileDescriptor().getFileDescriptor());
+ } catch (ErrnoException e) { }
+ }
+ }
+
+ private TestNetworkInterface setupTapInterface() {
+ final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ AtomicReference<TestNetworkInterface> iface = new AtomicReference<>();
+
+ inst.getUiAutomation().adoptShellPermissionIdentity();
+ try {
+ final TestNetworkManager tnm = (TestNetworkManager) inst.getContext().getSystemService(
+ Context.TEST_NETWORK_SERVICE);
+ iface.set(tnm.createTapInterface());
+ } finally {
+ inst.getUiAutomation().dropShellPermissionIdentity();
+ }
+
+ return iface.get();
+ }
+
+ private void setupTapInterfaces() {
+ // Create upstream test iface.
+ mUpstreamTestIface = setupTapInterface();
+ mUpstreamParams = InterfaceParams.getByName(mUpstreamTestIface.getInterfaceName());
+ assertNotNull(mUpstreamParams);
+ mUpstreamTapFd = mUpstreamTestIface.getFileDescriptor().getFileDescriptor();
+ mUpstreamPacketReader = new TapPacketReader(mHandler, mUpstreamTapFd,
+ DATA_BUFFER_LEN);
+ mHandler.post(mUpstreamPacketReader::start);
+
+ // Create tethered test iface.
+ mTetheredTestIface = setupTapInterface();
+ mTetheredParams = InterfaceParams.getByName(mTetheredTestIface.getInterfaceName());
+ assertNotNull(mTetheredParams);
+ mTetheredTapFd = mTetheredTestIface.getFileDescriptor().getFileDescriptor();
+ mTetheredPacketReader = new TapPacketReader(mHandler, mTetheredTapFd,
+ DATA_BUFFER_LEN);
+ mHandler.post(mTetheredPacketReader::start);
+ }
+
+ private static final int IPV6_HEADER_LEN = 40;
+ private static final int ETH_HEADER_LEN = 14;
+ private static final int ICMPV6_NA_NS_LEN = 24;
+ private static final int LL_TARGET_OPTION_LEN = 8;
+ private static final int ICMPV6_CHECKSUM_OFFSET = 2;
+ private static final int ETHER_TYPE_IPV6 = 0x86dd;
+
+ // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
+ private static int checksumFold(int sum) {
+ while (sum > 0xffff) {
+ sum = (sum >> 16) + (sum & 0xffff);
+ }
+ return sum;
+ }
+
+ // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
+ private static short checksumAdjust(short checksum, short oldWord, short newWord) {
+ checksum = (short) ~checksum;
+ int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord));
+ return (short) ~tempSum;
+ }
+
+ // TODO: move the IpUtils code to frameworks/lib/net and link it statically.
+ private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset,
+ int transportLen) {
+ // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses
+ // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental
+ // checksum adjustment for the change in the next header byte.
+ short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen);
+ return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6);
+ }
+
+ private static ByteBuffer createDadPacket(int type) {
+ // Refer to buildArpPacket()
+ int icmpLen = ICMPV6_NA_NS_LEN
+ + (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT
+ ? LL_TARGET_OPTION_LEN : 0);
+ final ByteBuffer buf = ByteBuffer.allocate(icmpLen + IPV6_HEADER_LEN + ETH_HEADER_LEN);
+
+ // Ethernet header.
+ final MacAddress srcMac = MacAddress.fromString("33:33:ff:66:77:88");
+ buf.put(srcMac.toByteArray());
+ final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06");
+ buf.put(dstMac.toByteArray());
+ buf.putShort((short) ETHER_TYPE_IPV6);
+
+ // IPv6 header
+ byte[] version = {(byte) 0x60, 0x00, 0x00, 0x00};
+ buf.put(version); // Version
+ buf.putShort((byte) icmpLen); // Length
+ buf.put((byte) IPPROTO_ICMPV6); // Next header
+ buf.put((byte) 0xff); // Hop limit
+
+ final byte[] target =
+ InetAddresses.parseNumericAddress("fe80::1122:3344:5566:7788").getAddress();
+ final byte[] src;
+ final byte[] dst;
+ if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION) {
+ src = InetAddresses.parseNumericAddress("::").getAddress();
+ dst = InetAddresses.parseNumericAddress("ff02::1:ff66:7788").getAddress();
+ } else {
+ src = target;
+ dst = TetheringUtils.ALL_NODES;
+ }
+ buf.put(src);
+ buf.put(dst);
+
+ // ICMPv6 Header
+ buf.put((byte) type); // Type
+ buf.put((byte) 0x00); // Code
+ buf.putShort((short) 0); // Checksum
+ buf.putInt(0); // Reserved
+ buf.put(target);
+
+ if (type == NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT) {
+ //NA packet has LL target address
+ //ICMPv6 Option
+ buf.put((byte) 0x02); // Type
+ buf.put((byte) 0x01); // Length
+ byte[] ll_target = MacAddress.fromString("01:02:03:04:05:06").toByteArray();
+ buf.put(ll_target);
+ }
+
+ // Populate checksum field
+ final int transportOffset = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+ final short checksum = icmpv6Checksum(buf, ETH_HEADER_LEN, transportOffset, icmpLen);
+ buf.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum);
+
+ buf.flip();
+ return buf;
+ }
+
+ private DadProxy setupProxy() throws Exception {
+ DadProxy proxy = new DadProxy(mHandler, mTetheredParams);
+ mHandler.post(() -> proxy.setUpstreamIface(mUpstreamParams));
+
+ // Upstream iface is added to local network to simplify test case.
+ // Otherwise the test needs to create and destroy a network for the upstream iface.
+ sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mUpstreamParams.name);
+ sNetd.networkAddInterface(INetd.LOCAL_NET_ID, mTetheredParams.name);
+
+ return proxy;
+ }
+
+ // TODO: change to assert.
+ private boolean waitForPacket(ByteBuffer packet, TapPacketReader reader) {
+ byte[] p;
+
+ while ((p = reader.popPacket(PACKET_TIMEOUT_MS)) != null) {
+ final ByteBuffer buffer = ByteBuffer.wrap(p);
+
+ if (buffer.compareTo(packet) == 0) return true;
+ }
+ return false;
+ }
+
+ private void updateDstMac(ByteBuffer buf, MacAddress mac) {
+ buf.put(mac.toByteArray());
+ buf.rewind();
+ }
+ private void updateSrcMac(ByteBuffer buf, InterfaceParams ifaceParams) {
+ buf.position(ETHER_SRC_ADDR_OFFSET);
+ buf.put(ifaceParams.macAddr.toByteArray());
+ buf.rewind();
+ }
+
+ @Test
+ public void testNaForwardingFromUpstreamToTether() throws Exception {
+ ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
+
+ mUpstreamPacketReader.sendResponse(na);
+ updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
+ updateSrcMac(na, mTetheredParams);
+ assertTrue(waitForPacket(na, mTetheredPacketReader));
+ }
+
+ @Test
+ // TODO: remove test once DAD works in both directions.
+ public void testNaForwardingFromTetherToUpstream() throws Exception {
+ ByteBuffer na = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_ADVERTISEMENT);
+
+ mTetheredPacketReader.sendResponse(na);
+ updateDstMac(na, MacAddress.fromString("33:33:00:00:00:01"));
+ updateSrcMac(na, mTetheredParams);
+ assertFalse(waitForPacket(na, mUpstreamPacketReader));
+ }
+
+ @Test
+ public void testNsForwardingFromTetherToUpstream() throws Exception {
+ ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
+
+ mTetheredPacketReader.sendResponse(ns);
+ updateSrcMac(ns, mUpstreamParams);
+ assertTrue(waitForPacket(ns, mUpstreamPacketReader));
+ }
+
+ @Test
+ // TODO: remove test once DAD works in both directions.
+ public void testNsForwardingFromUpstreamToTether() throws Exception {
+ ByteBuffer ns = createDadPacket(NeighborPacketForwarder.ICMPV6_NEIGHBOR_SOLICITATION);
+
+ mUpstreamPacketReader.sendResponse(ns);
+ updateSrcMac(ns, mUpstreamParams);
+ assertFalse(waitForPacket(ns, mTetheredPacketReader));
+ }
+}
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt
index ec2d2b0..7ed8963 100644
--- a/packages/Tethering/tests/unit/jarjar-rules.txt
+++ b/packages/Tethering/tests/unit/jarjar-rules.txt
@@ -9,3 +9,8 @@
rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
+
+# TODO: either stop using frameworks-base-testutils or remove the unit test classes it contains.
+# TestableLooper from "testables" can be used instead of TestLooper from frameworks-base-testutils.
+zap android.os.test.TestLooperTest*
+zap com.android.test.filters.SelectTestTests*
\ No newline at end of file
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 3b72b5b..1a976ad 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -86,6 +86,7 @@
import android.net.util.InterfaceSet;
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
+import android.os.Build;
import android.os.Handler;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -100,8 +101,12 @@
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import com.android.networkstack.tethering.TetheringConfiguration;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -120,6 +125,9 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IpServerTest {
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
private static final String IFACE_NAME = "testnet1";
private static final String UPSTREAM_IFACE = "upstream0";
private static final String UPSTREAM_IFACE2 = "upstream1";
@@ -132,6 +140,11 @@
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
+ private static final InterfaceParams UPSTREAM_IFACE_PARAMS = new InterfaceParams(
+ UPSTREAM_IFACE, UPSTREAM_IFINDEX, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
+ private static final InterfaceParams UPSTREAM_IFACE_PARAMS2 = new InterfaceParams(
+ UPSTREAM_IFACE2, UPSTREAM_IFINDEX2, MacAddress.ALL_ZEROS_ADDRESS,
+ 1500 /* defaultMtu */);
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
@@ -142,6 +155,7 @@
@Mock private IpServer.Callback mCallback;
@Mock private SharedLog mSharedLog;
@Mock private IDhcpServer mDhcpServer;
+ @Mock private DadProxy mDadProxy;
@Mock private RouterAdvertisementDaemon mRaDaemon;
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IpServer.Dependencies mDependencies;
@@ -165,8 +179,11 @@
private void initStateMachine(int interfaceType, boolean usingLegacyDhcp,
boolean usingBpfOffload) throws Exception {
+ when(mDependencies.getDadProxy(any(), any())).thenReturn(mDadProxy);
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+ when(mDependencies.getInterfaceParams(UPSTREAM_IFACE)).thenReturn(UPSTREAM_IFACE_PARAMS);
+ when(mDependencies.getInterfaceParams(UPSTREAM_IFACE2)).thenReturn(UPSTREAM_IFACE_PARAMS2);
when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX);
when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2);
@@ -1103,4 +1120,78 @@
}
return true;
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void dadProxyUpdates() throws Exception {
+ InOrder inOrder = inOrder(mDadProxy);
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+
+ // Add an upstream without IPv6.
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(null);
+
+ // Add IPv6 to the upstream.
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+
+ // Change upstream.
+ // New linkproperties is needed, otherwise changing the iface has no impact.
+ LinkProperties lp2 = new LinkProperties();
+ lp2.setInterfaceName(UPSTREAM_IFACE2);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp2, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS2);
+
+ // Lose IPv6 on the upstream...
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE2, null, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(null);
+
+ // ... and regain it on a different upstream.
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+
+ // Lose upstream.
+ dispatchTetherConnectionChanged(null, null, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(null);
+
+ // Regain upstream.
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 0);
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+
+ // Stop tethering.
+ mIpServer.stop();
+ mLooper.dispatchAll();
+ }
+
+ private void checkDadProxyEnabled(boolean expectEnabled) throws Exception {
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
+ InOrder inOrder = inOrder(mDadProxy);
+ // Add IPv6 to the upstream.
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ if (expectEnabled) {
+ inOrder.verify(mDadProxy).setUpstreamIface(UPSTREAM_IFACE_PARAMS);
+ } else {
+ inOrder.verifyNoMoreInteractions();
+ }
+ // Stop tethering.
+ mIpServer.stop();
+ mLooper.dispatchAll();
+ if (expectEnabled) {
+ inOrder.verify(mDadProxy).stop();
+ }
+ else {
+ verify(mDependencies, never()).getDadProxy(any(), any());
+ }
+ }
+ @Test @IgnoreAfter(Build.VERSION_CODES.R)
+ public void testDadProxyUpdates_DisabledUpToR() throws Exception {
+ checkDadProxyEnabled(false);
+ }
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testDadProxyUpdates_EnabledAfterR() throws Exception {
+ checkDadProxyEnabled(true);
+ }
}
diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 1fe3840..df57020 100644
--- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -110,6 +110,7 @@
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
+import android.net.ip.DadProxy;
import android.net.ip.IpNeighborMonitor;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
@@ -196,6 +197,7 @@
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
+ @Mock private DadProxy mDadProxy;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IDhcpServer mDhcpServer;
@@ -280,6 +282,12 @@
public class MockIpServerDependencies extends IpServer.Dependencies {
@Override
+ public DadProxy getDadProxy(
+ Handler handler, InterfaceParams ifParams) {
+ return mDadProxy;
+ }
+
+ @Override
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
InterfaceParams ifParams) {
return mRouterAdvertisementDaemon;
@@ -832,6 +840,7 @@
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
+ verify(mDadProxy, never()).setUpstreamIface(notNull());
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
@@ -858,6 +867,8 @@
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
+ // TODO: add interfaceParams to compare in verify.
+ verify(mDadProxy, times(1)).setUpstreamIface(notNull());
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
}
@@ -874,6 +885,7 @@
any(), any());
sendIPv6TetherUpdates(upstreamState);
+ verify(mDadProxy, times(1)).setUpstreamIface(notNull());
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
}
@@ -891,6 +903,7 @@
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
sendIPv6TetherUpdates(upstreamState);
+ verify(mDadProxy, times(1)).setUpstreamIface(notNull());
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
verify(mNetd, times(1)).tetherApplyDnsInterfaces();
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 9495fb5..ff79469 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -730,7 +730,8 @@
case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
- case WindowManager.LayoutParams.TYPE_SCREENSHOT: {
+ case WindowManager.LayoutParams.TYPE_SCREENSHOT:
+ case WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY: {
return AccessibilityWindowInfo.TYPE_SYSTEM;
}
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index b7fed87..958c15c 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -50,5 +50,10 @@
* Informs battery stats of binder stats for the given work source UID.
*/
public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
- Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids);
+ Collection<BinderCallsStats.CallStat> callStats);
+
+ /**
+ * Informs battery stats of native thread IDs of threads taking incoming binder calls.
+ */
+ public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index c951359..66ac889 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -49,6 +49,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
public class BinderCallsStatsService extends Binder {
@@ -273,7 +274,19 @@
BatteryStatsInternal batteryStatsInternal = getLocalService(
BatteryStatsInternal.class);
- mBinderCallsStats.setCallStatsObserver(batteryStatsInternal::noteBinderCallStats);
+ mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() {
+ @Override
+ public void noteCallStats(int workSourceUid, long incrementalCallCount,
+ Collection<BinderCallsStats.CallStat> callStats) {
+ batteryStatsInternal.noteBinderCallStats(workSourceUid,
+ incrementalCallCount, callStats);
+ }
+
+ @Override
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids);
+ }
+ });
// It needs to be called before mService.systemReady to make sure the observer is
// initialized before installing it.
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 8099f8f..e67b9d8 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -2,8 +2,7 @@
per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, junyulai@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
# Vibrator / Threads
-per-file VibratorService.java, DisplayThread.java = michaelwr@google.com
-per-file VibratorService.java, DisplayThread.java = ogunwale@google.com
+per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com
# Zram writeback
per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com
diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java
index 829fca6..9fc8f0b 100644
--- a/services/core/java/com/android/server/RescueParty.java
+++ b/services/core/java/com/android/server/RescueParty.java
@@ -454,10 +454,14 @@
public boolean mayObservePackage(String packageName) {
PackageManager pm = mContext.getPackageManager();
try {
- // A package is a Mainline module if this is non-null
+ // A package is a module if this is non-null
if (pm.getModuleInfo(packageName, 0) != null) {
return true;
}
+ } catch (PackageManager.NameNotFoundException ignore) {
+ }
+
+ try {
ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
return (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK;
} catch (PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index e342c1f..6d77486 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4641,6 +4641,11 @@
// make sure the OBB dir for the application is setup correctly, if it exists.
File[] packageObbDirs = userEnv.buildExternalStorageAppObbDirs(packageName);
for (File packageObbDir : packageObbDirs) {
+ if (packageObbDir.getPath().startsWith(
+ Environment.getDataPreloadsMediaDirectory().getPath())) {
+ Slog.i(TAG, "Skipping app data preparation for " + packageObbDir);
+ continue;
+ }
try {
mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid);
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index bcb7bfa..0135b9d 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -72,7 +72,6 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.DumpUtils;
-import com.android.server.SystemService.TargetUser;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
@@ -327,8 +326,16 @@
@Override
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
mCurrentUser = to.getUserIdentifier();
+ if (mNightMode == MODE_NIGHT_AUTO) persistComputedNightMode(from.getUserIdentifier());
getContext().getContentResolver().unregisterContentObserver(mSetupWizardObserver);
verifySetupWizardCompleted();
+ synchronized (mLock) {
+ // only update if the value is actually changed
+ if (updateNightModeFromSettingsLocked(getContext(), getContext().getResources(),
+ to.getUserIdentifier())) {
+ updateLocked(0, 0);
+ }
+ }
}
@Override
@@ -356,11 +363,10 @@
new IntentFilter(Intent.ACTION_DOCK_EVENT));
IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(mBatteryReceiver, batteryFilter);
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(mSettingsRestored,
new IntentFilter(Intent.ACTION_SETTING_RESTORED));
- context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
+ context.registerReceiver(mOnShutdown,
+ new IntentFilter(Intent.ACTION_SHUTDOWN));
updateConfigurationLocked();
applyConfigurationExternallyLocked();
}
@@ -407,6 +413,21 @@
publishLocalService(UiModeManagerInternal.class, mLocalService);
}
+ private final BroadcastReceiver mOnShutdown = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mNightMode == MODE_NIGHT_AUTO) {
+ persistComputedNightMode(mCurrentUser);
+ }
+ }
+ };
+
+ private void persistComputedNightMode(int userId) {
+ Secure.putIntForUser(getContext().getContentResolver(),
+ Secure.UI_NIGHT_MODE_LAST_COMPUTED, mComputedNightMode ? 1 : 0,
+ userId);
+ }
+
private final BroadcastReceiver mSettingsRestored = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -508,6 +529,10 @@
Secure.getLongForUser(context.getContentResolver(),
Secure.DARK_THEME_CUSTOM_END_TIME,
DEFAULT_CUSTOM_NIGHT_END_TIME.toNanoOfDay() / 1000L, userId) * 1000);
+ if (mNightMode == MODE_NIGHT_AUTO) {
+ mComputedNightMode = Secure.getIntForUser(context.getContentResolver(),
+ Secure.UI_NIGHT_MODE_LAST_COMPUTED, 0, userId) != 0;
+ }
}
return oldNightMode != mNightMode;
@@ -1630,18 +1655,4 @@
}
}
}
-
- private final class UserSwitchedReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- synchronized (mLock) {
- final int currentId = intent.getIntExtra(
- Intent.EXTRA_USER_HANDLE, USER_SYSTEM);
- // only update if the value is actually changed
- if (updateNightModeFromSettingsLocked(context, context.getResources(), currentId)) {
- updateLocked(0, 0);
- }
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/VibratorManagerService.java b/services/core/java/com/android/server/VibratorManagerService.java
new file mode 100644
index 0000000..2f35da7
--- /dev/null
+++ b/services/core/java/com/android/server/VibratorManagerService.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IVibratorManagerService;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/** System implementation of {@link IVibratorManagerService}. */
+public class VibratorManagerService extends IVibratorManagerService.Stub {
+ private static final String TAG = "VibratorManagerService";
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+ private final NativeWrapper mNativeWrapper;
+ private final int[] mVibratorIds;
+
+ static native long nativeInit();
+
+ static native long nativeGetFinalizer();
+
+ static native int[] nativeGetVibratorIds(long nativeServicePtr);
+
+ VibratorManagerService(Context context) {
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ VibratorManagerService(Context context, Injector injector) {
+ mContext = context;
+ mNativeWrapper = injector.getNativeWrapper();
+
+ mNativeWrapper.init();
+
+ int[] vibratorIds = mNativeWrapper.getVibratorIds();
+ mVibratorIds = vibratorIds == null ? new int[0] : vibratorIds;
+ }
+
+ @Override // Binder call
+ public int[] getVibratorIds() {
+ return Arrays.copyOf(mVibratorIds, mVibratorIds.length);
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
+ new VibratorManagerShellCommand(this).exec(this, in, out, err, args, cb, resultReceiver);
+ }
+
+ /** Point of injection for test dependencies */
+ @VisibleForTesting
+ static class Injector {
+
+ NativeWrapper getNativeWrapper() {
+ return new NativeWrapper();
+ }
+ }
+
+ /** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */
+ @VisibleForTesting
+ public static class NativeWrapper {
+
+ private long mNativeServicePtr = 0;
+
+ /** Returns native pointer to newly created controller and connects with HAL service. */
+ public void init() {
+ mNativeServicePtr = VibratorManagerService.nativeInit();
+ long finalizerPtr = VibratorManagerService.nativeGetFinalizer();
+
+ if (finalizerPtr != 0) {
+ NativeAllocationRegistry registry =
+ NativeAllocationRegistry.createMalloced(
+ VibratorManagerService.class.getClassLoader(), finalizerPtr);
+ registry.registerNativeAllocation(this, mNativeServicePtr);
+ }
+ }
+
+ /** Returns vibrator ids. */
+ public int[] getVibratorIds() {
+ return VibratorManagerService.nativeGetVibratorIds(mNativeServicePtr);
+ }
+ }
+
+ /** Provides limited functionality from {@link VibratorManagerService} as shell commands. */
+ private final class VibratorManagerShellCommand extends ShellCommand {
+
+ private final IBinder mToken;
+
+ private VibratorManagerShellCommand(IBinder token) {
+ mToken = token;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if ("list".equals(cmd)) {
+ return runListVibrators();
+ }
+ return handleDefaultCommands(cmd);
+ }
+
+ private int runListVibrators() {
+ try (PrintWriter pw = getOutPrintWriter();) {
+ if (mVibratorIds.length == 0) {
+ pw.println("No vibrator found");
+ } else {
+ for (int id : mVibratorIds) {
+ pw.println(id);
+ }
+ }
+ pw.println("");
+ return 0;
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter();) {
+ pw.println("Vibrator Manager commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" list");
+ pw.println(" Prints the id of device vibrators. This do not include any ");
+ pw.println(" connected input device.");
+ pw.println("");
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 687af10..cb6f616 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -385,15 +385,7 @@
mNativeWrapper = injector.getNativeWrapper();
mH = injector.createHandler(Looper.myLooper());
- long nativeServicePtr = mNativeWrapper.vibratorInit(this::onVibrationComplete);
- long finalizerPtr = mNativeWrapper.vibratorGetFinalizer();
-
- if (finalizerPtr != 0) {
- NativeAllocationRegistry registry =
- NativeAllocationRegistry.createMalloced(
- VibratorService.class.getClassLoader(), finalizerPtr);
- registry.registerNativeAllocation(this, nativeServicePtr);
- }
+ mNativeWrapper.vibratorInit(this::onVibrationComplete);
// Reset the hardware to a default state, in case this is a runtime
// restart instead of a fresh boot.
@@ -1746,18 +1738,17 @@
return VibratorService.vibratorExists(mNativeServicePtr);
}
- /**
- * Returns native pointer to newly created controller and initializes connection to vibrator
- * HAL service.
- */
- public long vibratorInit(OnCompleteListener listener) {
+ /** Initializes connection to vibrator HAL service. */
+ public void vibratorInit(OnCompleteListener listener) {
mNativeServicePtr = VibratorService.vibratorInit(listener);
- return mNativeServicePtr;
- }
+ long finalizerPtr = VibratorService.vibratorGetFinalizer();
- /** Returns pointer to native finalizer function to be called by GC. */
- public long vibratorGetFinalizer() {
- return VibratorService.vibratorGetFinalizer();
+ if (finalizerPtr != 0) {
+ NativeAllocationRegistry registry =
+ NativeAllocationRegistry.createMalloced(
+ VibratorService.class.getClassLoader(), finalizerPtr);
+ registry.registerNativeAllocation(this, mNativeServicePtr);
+ }
}
/** Turns vibrator on for given time. */
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index bfe04e6..91a3fb0 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -478,17 +478,18 @@
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
- int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification,
- String callingPackage, @Nullable String callingFeatureId, final int userId)
+ int callingPid, int callingUid, boolean fgRequired, String callingPackage,
+ @Nullable String callingFeatureId, final int userId)
throws TransactionTooLargeException {
return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
- hideFgNotification, callingPackage, callingFeatureId, userId, false);
+ callingPackage, callingFeatureId, userId, false, null);
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
- int callingPid, int callingUid, boolean fgRequired, boolean hideFgNotification,
+ int callingPid, int callingUid, boolean fgRequired,
String callingPackage, @Nullable String callingFeatureId, final int userId,
- boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
+ boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken)
+ throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
@@ -652,7 +653,6 @@
r.startRequested = true;
r.delayedStop = false;
r.fgRequired = fgRequired;
- r.hideFgNotification = hideFgNotification;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid));
@@ -733,7 +733,7 @@
}
}
if (allowBackgroundActivityStarts) {
- r.allowBgActivityStartsOnServiceStart();
+ r.allowBgActivityStartsOnServiceStart(backgroundActivityStartsToken);
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
@@ -2609,12 +2609,12 @@
private int getAllowMode(Intent service, @Nullable String callingPackage) {
if (callingPackage == null || service.getComponent() == null) {
- return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
+ return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
}
if (callingPackage.equals(service.getComponent().getPackageName())) {
- return ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL;
+ return ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
} else {
- return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
+ return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2de3e52..65334fd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1177,10 +1177,6 @@
final Injector mInjector;
- /** The package verifier app. */
- private String mPackageVerifier;
- private int mPackageVerifierUid = UserHandle.USER_NULL;
-
static final class ProcessChangeItem {
static final int CHANGE_ACTIVITIES = 1<<0;
static final int CHANGE_FOREGROUND_SERVICES = 1<<1;
@@ -1809,18 +1805,6 @@
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mService.mBatteryStatsService.systemServicesReady();
mService.mServices.systemServicesReady();
- mService.mPackageVerifier = ArrayUtils.firstOrNull(
- LocalServices.getService(PackageManagerInternal.class).getKnownPackageNames(
- PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM));
- if (mService.mPackageVerifier != null) {
- try {
- mService.mPackageVerifierUid =
- getContext().getPackageManager().getPackageUid(
- mService.mPackageVerifier, UserHandle.USER_SYSTEM);
- } catch (NameNotFoundException e) {
- Slog.wtf(TAG, "Package manager couldn't get package verifier uid", e);
- }
- }
} else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mService.startBroadcastObservers();
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -11233,10 +11217,10 @@
}
long kernelUsed = memInfo.getKernelUsedSizeKb();
final long ionHeap = Debug.getIonHeapsSizeKb();
- if (ionHeap > 0) {
+ final long ionPool = Debug.getIonPoolsSizeKb();
+ if (ionHeap >= 0 && ionPool >= 0) {
final long ionMapped = Debug.getIonMappedSizeKb();
final long ionUnmapped = ionHeap - ionMapped;
- final long ionPool = Debug.getIonPoolsSizeKb();
pw.print(" ION: ");
pw.print(stringifyKBSize(ionHeap + ionPool));
pw.print(" (");
@@ -12033,10 +12017,10 @@
memInfoBuilder.append("\n");
long kernelUsed = memInfo.getKernelUsedSizeKb();
final long ionHeap = Debug.getIonHeapsSizeKb();
- if (ionHeap > 0) {
+ final long ionPool = Debug.getIonPoolsSizeKb();
+ if (ionHeap >= 0 && ionPool >= 0) {
final long ionMapped = Debug.getIonMappedSizeKb();
final long ionUnmapped = ionHeap - ionMapped;
- final long ionPool = Debug.getIonPoolsSizeKb();
memInfoBuilder.append(" ION: ");
memInfoBuilder.append(stringifyKBSize(ionHeap + ionPool));
memInfoBuilder.append("\n");
@@ -12359,8 +12343,8 @@
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType, boolean requireForeground, boolean hideForegroundNotification,
- String callingPackage, String callingFeatureId, int userId)
+ String resolvedType, boolean requireForeground, String callingPackage,
+ String callingFeatureId, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
@@ -12372,27 +12356,17 @@
throw new IllegalArgumentException("callingPackage cannot be null");
}
- final int callingUid = Binder.getCallingUid();
- if (requireForeground && hideForegroundNotification) {
- if (!UserHandle.isSameApp(callingUid, mPackageVerifierUid)
- || !callingPackage.equals(mPackageVerifier)) {
- throw new IllegalArgumentException(
- "Only the package verifier can hide its foreground service notification");
- }
- Slog.i(TAG, "Foreground service notification hiding requested by " + callingPackage);
- }
-
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
- requireForeground, hideForegroundNotification,
- callingPackage, callingFeatureId, userId);
+ requireForeground, callingPackage, callingFeatureId, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -16305,8 +16279,9 @@
ComponentName res;
try {
res = mServices.startServiceLocked(null, service,
- resolvedType, -1, uid, fgRequired, false, callingPackage,
- callingFeatureId, userId, allowBackgroundActivityStarts);
+ resolvedType, -1, uid, fgRequired, callingPackage,
+ callingFeatureId, userId, allowBackgroundActivityStarts,
+ backgroundActivityStartsToken);
} finally {
Binder.restoreCallingIdentity(origId);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 1119728..d346430 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -655,7 +655,7 @@
pw.println("Starting service: " + intent);
pw.flush();
ComponentName cn = mInterface.startService(null, intent, intent.getType(),
- asForeground, false, SHELL_PACKAGE_NAME, null, mUserId);
+ asForeground, SHELL_PACKAGE_NAME, null, mUserId);
if (cn == null) {
err.println("Error: Not found; no service started.");
return -1;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 808a699..76125aa 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -240,14 +240,20 @@
@Override
public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
- Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
+ Collection<BinderCallsStats.CallStat> callStats) {
synchronized (BatteryStatsService.this.mLock) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- mStats::noteBinderCallStats, workSourceUid, incrementatCallCount,
- callStats, binderThreadNativeTids,
+ mStats::noteBinderCallStats, workSourceUid, incrementatCallCount, callStats,
SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()));
}
}
+
+ @Override
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ synchronized (BatteryStatsService.this.mLock) {
+ mStats.noteBinderThreadNativeIds(binderThreadNativeTids);
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 93105da..57f8112 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1686,7 +1686,7 @@
// that request - we don't want the token to be swept from under our feet...
mHandler.removeCallbacksAndMessages(msgToken);
// ...then add the token
- proc.addAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
+ proc.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index f1e12c5..2dced8d 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -77,6 +77,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
/**
@@ -1338,13 +1339,15 @@
* {@param originatingToken} if you have one such originating token, this is useful for tracing
* back the grant in the case of the notification token.
*/
- void addAllowBackgroundActivityStartsToken(Binder entity, @Nullable IBinder originatingToken) {
- if (entity == null) return;
- mWindowProcessController.addAllowBackgroundActivityStartsToken(entity, originatingToken);
+ void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
+ @Nullable IBinder originatingToken) {
+ Objects.requireNonNull(entity);
+ mWindowProcessController.addOrUpdateAllowBackgroundActivityStartsToken(entity,
+ originatingToken);
}
void removeAllowBackgroundActivityStartsToken(Binder entity) {
- if (entity == null) return;
+ Objects.requireNonNull(entity);
mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity);
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 55e0f2e..4cdf661 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -22,6 +22,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -104,7 +105,6 @@
boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
boolean delayed; // are we waiting to start this service in the background?
boolean fgRequired; // is the service required to go foreground after starting?
- boolean hideFgNotification; // Hide the fg service notification
boolean fgWaiting; // is a timeout for going foreground already scheduled?
boolean isForeground; // is service currently in foreground mode?
int foregroundId; // Notification ID of last foreground req.
@@ -139,6 +139,12 @@
// used to clean up the state of mIsAllowedBgActivityStartsByStart after a timeout
private Runnable mCleanUpAllowBgActivityStartsByStartCallback;
private ProcessRecord mAppForAllowingBgActivityStartsByStart;
+ // These are the originating tokens that currently allow bg activity starts by service start.
+ // This is used to trace back the grant when starting activities. We only pass such token to the
+ // ProcessRecord if it's the *only* cause for bg activity starts exemption, otherwise we pass
+ // null.
+ @GuardedBy("ams")
+ private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>();
// allow while-in-use permissions in foreground service or not.
// while-in-use permissions in FGS started from background might be restricted.
@@ -588,7 +594,8 @@
? _proc : null;
if (mIsAllowedBgActivityStartsByStart
|| mIsAllowedBgActivityStartsByBinding) {
- _proc.addAllowBackgroundActivityStartsToken(this, null);
+ _proc.addOrUpdateAllowBackgroundActivityStartsToken(this,
+ getExclusiveOriginatingToken());
} else {
_proc.removeAllowBackgroundActivityStartsToken(this);
}
@@ -679,10 +686,8 @@
}
void setAllowedBgActivityStartsByBinding(boolean newValue) {
- if (mIsAllowedBgActivityStartsByBinding != newValue) {
- mIsAllowedBgActivityStartsByBinding = newValue;
- updateParentProcessBgActivityStartsToken();
- }
+ mIsAllowedBgActivityStartsByBinding = newValue;
+ updateParentProcessBgActivityStartsToken();
}
/**
@@ -691,7 +696,8 @@
* timeout. Note that the ability for starting background activities persists for the process
* even if the service is subsequently stopped.
*/
- void allowBgActivityStartsOnServiceStart() {
+ void allowBgActivityStartsOnServiceStart(@Nullable IBinder originatingToken) {
+ mBgActivityStartsByStartOriginatingTokens.add(originatingToken);
setAllowedBgActivityStartsByStart(true);
if (app != null) {
mAppForAllowingBgActivityStartsByStart = app;
@@ -701,32 +707,49 @@
if (mCleanUpAllowBgActivityStartsByStartCallback == null) {
mCleanUpAllowBgActivityStartsByStartCallback = () -> {
synchronized (ams) {
- if (app == mAppForAllowingBgActivityStartsByStart) {
- // The process we allowed is still running the service. We remove
- // the ability by start, but it may still be allowed via bound connections.
- setAllowedBgActivityStartsByStart(false);
- } else if (mAppForAllowingBgActivityStartsByStart != null) {
- // The process we allowed is not running the service. It therefore can't be
- // bound so we can unconditionally remove the ability.
- mAppForAllowingBgActivityStartsByStart
- .removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
+ mBgActivityStartsByStartOriginatingTokens.remove(0);
+ if (!mBgActivityStartsByStartOriginatingTokens.isEmpty()) {
+ // There are other callbacks in the queue, let's just update the originating
+ // token
+ if (mIsAllowedBgActivityStartsByStart) {
+ mAppForAllowingBgActivityStartsByStart
+ .addOrUpdateAllowBackgroundActivityStartsToken(
+ this, getExclusiveOriginatingToken());
+ } else {
+ Slog.wtf(TAG,
+ "Service callback to revoke bg activity starts by service "
+ + "start triggered but "
+ + "mIsAllowedBgActivityStartsByStart = false. This "
+ + "should never happen.");
+ }
+ } else {
+ // Last callback on the queue
+ if (app == mAppForAllowingBgActivityStartsByStart) {
+ // The process we allowed is still running the service. We remove
+ // the ability by start, but it may still be allowed via bound
+ // connections.
+ setAllowedBgActivityStartsByStart(false);
+ } else if (mAppForAllowingBgActivityStartsByStart != null) {
+ // The process we allowed is not running the service. It therefore can't
+ // be bound so we can unconditionally remove the ability.
+ mAppForAllowingBgActivityStartsByStart
+ .removeAllowBackgroundActivityStartsToken(ServiceRecord.this);
+ }
+ mAppForAllowingBgActivityStartsByStart = null;
}
- mAppForAllowingBgActivityStartsByStart = null;
}
};
}
- // if there's a request pending from the past, drop it before scheduling a new one
- ams.mHandler.removeCallbacks(mCleanUpAllowBgActivityStartsByStartCallback);
+ // Existing callbacks will only update the originating token, only when the last callback is
+ // executed is the grant revoked.
ams.mHandler.postDelayed(mCleanUpAllowBgActivityStartsByStartCallback,
ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
}
private void setAllowedBgActivityStartsByStart(boolean newValue) {
- if (mIsAllowedBgActivityStartsByStart != newValue) {
- mIsAllowedBgActivityStartsByStart = newValue;
- updateParentProcessBgActivityStartsToken();
- }
+ mIsAllowedBgActivityStartsByStart = newValue;
+ updateParentProcessBgActivityStartsToken();
}
/**
@@ -736,8 +759,8 @@
* {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord
* should be contributing as a token in parent ProcessRecord.
*
- * @see com.android.server.am.ProcessRecord#addAllowBackgroundActivityStartsToken(Binder,
- * IBinder)
+ * @see com.android.server.am.ProcessRecord#addOrUpdateAllowBackgroundActivityStartsToken(
+ * Binder, IBinder)
* @see com.android.server.am.ProcessRecord#removeAllowBackgroundActivityStartsToken(Binder)
*/
private void updateParentProcessBgActivityStartsToken() {
@@ -747,12 +770,37 @@
if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) {
// if the token is already there it's safe to "re-add it" - we're dealing with
// a set of Binder objects
- app.addAllowBackgroundActivityStartsToken(this, null);
+ app.addOrUpdateAllowBackgroundActivityStartsToken(this, getExclusiveOriginatingToken());
} else {
app.removeAllowBackgroundActivityStartsToken(this);
}
}
+ /**
+ * Returns the originating token if that's the only reason background activity starts are
+ * allowed. In order for that to happen the service has to be allowed only due to starts, since
+ * bindings are not associated with originating tokens, and all the start tokens have to be the
+ * same and there can't be any null originating token in the queue.
+ *
+ * Originating tokens are optional, so the caller could provide null when it allows bg activity
+ * starts.
+ */
+ @Nullable
+ private IBinder getExclusiveOriginatingToken() {
+ if (mIsAllowedBgActivityStartsByBinding
+ || mBgActivityStartsByStartOriginatingTokens.isEmpty()) {
+ return null;
+ }
+ IBinder firstToken = mBgActivityStartsByStartOriginatingTokens.get(0);
+ for (int i = 1, n = mBgActivityStartsByStartOriginatingTokens.size(); i < n; i++) {
+ IBinder token = mBgActivityStartsByStartOriginatingTokens.get(i);
+ if (token != firstToken) {
+ return null;
+ }
+ }
+ return firstToken;
+ }
+
@GuardedBy("ams")
void updateKeepWarmLocked() {
mKeepWarming = ams.mConstants.KEEP_WARMING_SERVICES.contains(name)
@@ -837,9 +885,6 @@
}
public void postNotification() {
- if (hideFgNotification) {
- return;
- }
final int appUid = appInfo.uid;
final int appPid = app.pid;
if (foregroundId != 0 && foregroundNoti != null) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3dfbcc7..eb60573 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -23,11 +23,10 @@
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
-import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL;
-import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
-import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
@@ -1912,12 +1911,11 @@
callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
// If the caller does not have either permission, they are always doomed.
allow = false;
- } else if (allowMode == ALLOW_NON_FULL
- || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL) {
+ } else if (allowMode == ALLOW_NON_FULL) {
// We are blanket allowing non-full access, you lucky caller!
allow = true;
- } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE_OR_FULL
- || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL) {
+ } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE
+ || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
// We may or may not allow this depending on whether the two users are
// in the same profile.
allow = isSameProfileGroup;
@@ -1944,15 +1942,12 @@
builder.append("; this requires ");
builder.append(INTERACT_ACROSS_USERS_FULL);
if (allowMode != ALLOW_FULL_ONLY) {
- if (allowMode == ALLOW_NON_FULL
- || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL
- || isSameProfileGroup) {
+ if (allowMode == ALLOW_NON_FULL || isSameProfileGroup) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_USERS);
}
if (isSameProfileGroup
- && (allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL
- || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL)) {
+ && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_PROFILES);
}
@@ -1979,8 +1974,7 @@
private boolean canInteractWithAcrossProfilesPermission(
int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid,
String callingPackage) {
- if (allowMode != ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL
- && allowMode != ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL) {
+ if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
return false;
}
if (!isSameProfileGroup) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6099e52..668713f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,7 +19,6 @@
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
-import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL;
import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
@@ -37,7 +36,6 @@
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
-import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
@@ -131,7 +129,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
-import android.util.EventLog;
import android.util.KeyValueListParser;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -165,7 +162,6 @@
import com.android.server.LockGuard;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemServiceManager;
-import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageList;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -2203,11 +2199,8 @@
+ " by uid " + Binder.getCallingUid());
}
- int userId = UserHandle.getUserId(uid);
-
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
- verifyIncomingUser(userId);
code = AppOpsManager.opToSwitch(code);
if (permissionPolicyCallback == null) {
@@ -2252,11 +2245,6 @@
scheduleWriteLocked();
}
uidState.evalForegroundOps(mOpModeWatchers);
-
- if (code == OP_INTERACT_ACROSS_PROFILES) {
- // Invalidate package info cache as the visibility of packages might have changed
- PackageManager.invalidatePackageInfoCache();
- }
}
notifyOpChangedForAllPkgsInUid(code, uid, false, permissionPolicyCallback);
@@ -2462,12 +2450,8 @@
private void setMode(int code, int uid, @NonNull String packageName, int mode,
@Nullable IAppOpsCallback permissionPolicyCallback) {
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
-
- int userId = UserHandle.getUserId(uid);
-
verifyIncomingOp(code);
- verifyIncomingUser(userId);
- verifyIncomingPackage(packageName, userId);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
ArraySet<ModeCallback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
@@ -2729,9 +2713,6 @@
if (changed) {
scheduleFastWriteLocked();
-
- // Invalidate package info cache as the visibility of packages might have changed
- PackageManager.invalidatePackageInfoCache();
}
}
if (callbacks != null) {
@@ -2890,11 +2871,8 @@
private int checkOperationImpl(int code, int uid, String packageName,
boolean raw) {
- int userId = UserHandle.getUserId(uid);
-
verifyIncomingOp(code);
- verifyIncomingUser(userId);
- verifyIncomingPackage(packageName, userId);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -3013,15 +2991,10 @@
String proxiedAttributionTag, int proxyUid, String proxyPackageName,
String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message,
boolean shouldCollectMessage) {
- int proxiedUserId = UserHandle.getUserId(proxiedUid);
- int proxyUserId = UserHandle.getUserId(proxyUid);
-
verifyIncomingUid(proxyUid);
verifyIncomingOp(code);
- verifyIncomingUser(proxiedUserId);
- verifyIncomingUser(proxyUserId);
- verifyIncomingPackage(proxiedPackageName, proxiedUserId);
- verifyIncomingPackage(proxyPackageName, proxyUserId);
+ verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
+ verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolveProxyPackageName == null) {
@@ -3071,12 +3044,9 @@
private int noteOperationImpl(int code, int uid, @Nullable String packageName,
@Nullable String attributionTag, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage) {
- int userId = UserHandle.getUserId(uid);
-
verifyIncomingUid(uid);
verifyIncomingOp(code);
- verifyIncomingUser(userId);
- verifyIncomingPackage(packageName, userId);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -3453,12 +3423,9 @@
public int startOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
String message, boolean shouldCollectMessage) {
- int userId = UserHandle.getUserId(uid);
-
verifyIncomingUid(uid);
verifyIncomingOp(code);
- verifyIncomingUser(userId);
- verifyIncomingPackage(packageName, userId);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -3550,12 +3517,9 @@
@Override
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag) {
- int userId = UserHandle.getUserId(uid);
-
verifyIncomingUid(uid);
verifyIncomingOp(code);
- verifyIncomingUser(userId);
- verifyIncomingPackage(packageName, userId);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -3784,33 +3748,6 @@
}
}
- private void verifyIncomingUser(@UserIdInt int userId) {
- int callingUid = Binder.getCallingUid();
- int callingUserId = UserHandle.getUserId(callingUid);
- int callingPid = Binder.getCallingPid();
-
- if (callingUserId != userId) {
- // Prevent endless loop between when checking appops inside of handleIncomingUser
- if (Binder.getCallingPid() == ActivityManagerService.MY_PID) {
- return;
- }
- long token = Binder.clearCallingIdentity();
- try {
- try {
- LocalServices.getService(ActivityManagerInternal.class).handleIncomingUser(
- callingPid, callingUid, userId, /* allowAll */ false,
- ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL, "appop operation", null);
- } catch (Exception e) {
- EventLog.writeEvent(0x534e4554, "153996875", "appop", userId);
-
- throw e;
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-
private @Nullable UidState getUidStateLocked(int uid, boolean edit) {
UidState uidState = mUidStates.get(uid);
if (uidState == null) {
@@ -5890,11 +5827,8 @@
return false;
}
}
- int userId = UserHandle.getUserId(uid);
-
verifyIncomingOp(code);
- verifyIncomingUser(userId);
- verifyIncomingPackage(packageName, userId);
+ verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
final String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index a3e1b7a..84de25c 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -7,9 +7,6 @@
"name": "CtsAppOps2TestCases"
},
{
- "name": "CtsAppOpHostTestCases"
- },
- {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6110759..f1561cab 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6735,14 +6735,17 @@
if (msg.obj == null) {
break;
}
- // If the app corresponding to this mode death handler object is not
- // capturing or playing audio anymore after 3 seconds, remove it
- // from the stack. Otherwise, check again in 3 seconds.
+ // If no other app is currently owning the audio mode and
+ // the app corresponding to this mode death handler object is still in the
+ // mode owner stack but not capturing or playing audio after 3 seconds,
+ // remove it from the stack.
+ // Otherwise, check again in 3 seconds.
SetModeDeathHandler h = (SetModeDeathHandler) msg.obj;
if (mSetModeDeathHandlers.indexOf(h) < 0) {
break;
}
- if (mRecordMonitor.isRecordingActiveForUid(h.getUid())
+ if (getModeOwnerUid() != h.getUid()
+ || mRecordMonitor.isRecordingActiveForUid(h.getUid())
|| mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) {
sendMsg(mAudioHandler,
MSG_CHECK_MODE_FOR_UID,
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index ded0f9a3..8de31d9 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -450,6 +450,7 @@
}
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("BtHelper.this")
private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
diff --git a/services/core/java/com/android/server/biometrics/SensorConfig.java b/services/core/java/com/android/server/biometrics/SensorConfig.java
index 7743f1c..a83f67a 100644
--- a/services/core/java/com/android/server/biometrics/SensorConfig.java
+++ b/services/core/java/com/android/server/biometrics/SensorConfig.java
@@ -16,13 +16,15 @@
package com.android.server.biometrics;
+import android.hardware.biometrics.BiometricManager;
+
/**
* Parsed sensor config. See core/res/res/values/config.xml config_biometric_sensors
*/
public class SensorConfig {
public final int id;
final int modality;
- final int strength;
+ @BiometricManager.Authenticators.Types public final int strength;
public SensorConfig(String config) {
String[] elems = config.split(":");
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 88fd444..c300169 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -48,6 +48,7 @@
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.hardware.biometrics.IBiometricService;
import android.hardware.biometrics.PromptInfo;
+import android.hardware.biometrics.SensorProperties;
import android.os.Binder;
import android.os.Build;
import android.os.RemoteException;
@@ -444,4 +445,22 @@
}
return false;
}
+
+ /**
+ * Converts from {@link BiometricManager.Authenticators} biometric strength to the internal
+ * {@link SensorProperties} strength.
+ */
+ public static @SensorProperties.Strength int authenticatorStrengthToPropertyStrength(
+ @BiometricManager.Authenticators.Types int strength) {
+ switch (strength) {
+ case BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE:
+ return SensorProperties.STRENGTH_CONVENIENCE;
+ case BiometricManager.Authenticators.BIOMETRIC_WEAK:
+ return SensorProperties.STRENGTH_WEAK;
+ case BiometricManager.Authenticators.BIOMETRIC_STRONG:
+ return SensorProperties.STRENGTH_STRONG;
+ default:
+ throw new IllegalArgumentException("Unknown strength: " + strength);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
index 1613d3b..c17bc91 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -26,6 +26,7 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
@@ -60,7 +61,6 @@
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalConsumer;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintUpdateActiveUserClient;
import org.json.JSONArray;
import org.json.JSONException;
@@ -279,6 +279,7 @@
};
Face10(@NonNull Context context, int sensorId,
+ @BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
final boolean supportsSelfIllumination = context.getResources()
.getBoolean(R.bool.config_faceAuthSupportsSelfIllumination);
@@ -402,7 +403,7 @@
}
/**
- * Schedules the {@link FingerprintUpdateActiveUserClient} without posting the work onto the
+ * Schedules the {@link FaceUpdateActiveUserClient} without posting the work onto the
* handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
* invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
* this operation on the same lambda/runnable as those operations so that the ordering is
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index bbee6cd..3318bcb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -34,7 +34,7 @@
public FaceAuthenticator(IFaceService faceService, SensorConfig config)
throws RemoteException {
mFaceService = faceService;
- mFaceService.initializeConfiguration(config.id);
+ mFaceService.initializeConfiguration(config.id, config.strength);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index c6664f4..82dc0d0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
@@ -308,9 +309,10 @@
}
@Override // Binder call
- public void initializeConfiguration(int sensorId) {
+ public void initializeConfiguration(int sensorId,
+ @BiometricManager.Authenticators.Types int strength) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFace10 = new Face10(getContext(), sensorId, mLockoutResetDispatcher);
+ mFace10 = new Face10(getContext(), sensorId, strength, mLockoutResetDispatcher);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index 21a46d5..5219df4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -34,7 +34,7 @@
public FingerprintAuthenticator(IFingerprintService fingerprintService, SensorConfig config)
throws RemoteException {
mFingerprintService = fingerprintService;
- mFingerprintService.initializeConfiguration(config.id);
+ mFingerprintService.initializeConfiguration(config.id, config.strength);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 7c7da11..2903b9970 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -57,6 +57,8 @@
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21;
+import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21UdfpsMock;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -384,7 +386,7 @@
}
@Override // Binder call
- public void initializeConfiguration(int sensorId) {
+ public void initializeConfiguration(int sensorId, int strength) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
if ((Build.IS_USERDEBUG || Build.IS_ENG)
@@ -393,9 +395,9 @@
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
UserHandle.USER_CURRENT) != 0) {
mFingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId,
- mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+ strength, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
} else {
- mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId,
+ mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId, strength,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
similarity index 92%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index 7e62329..c87bfec 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +27,7 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
@@ -63,6 +64,8 @@
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalConsumer;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import org.json.JSONArray;
import org.json.JSONException;
@@ -80,7 +83,7 @@
* Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
* its extended minor versions.
*/
-class Fingerprint21 implements IHwBinder.DeathRecipient {
+public class Fingerprint21 implements IHwBinder.DeathRecipient {
private static final String TAG = "Fingerprint21";
private static final int ENROLL_TIMEOUT_SEC = 60;
@@ -296,6 +299,7 @@
Fingerprint21(@NonNull Context context, @NonNull BiometricScheduler scheduler,
@NonNull Handler handler, int sensorId,
+ @BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull HalResultController controller) {
mContext = context;
@@ -335,25 +339,27 @@
}
final @FingerprintSensorProperties.SensorType int sensorType =
- isUdfps ? FingerprintSensorProperties.TYPE_UDFPS
+ isUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
: FingerprintSensorProperties.TYPE_REAR;
// resetLockout is controlled by the framework, so hardwareAuthToken is not required
final boolean resetLockoutRequiresHardwareAuthToken = false;
- final int maxTemplatesAllowed = mContext.getResources()
+ final int maxEnrollmentsPerUser = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
- mSensorProperties = new FingerprintSensorProperties(sensorId, sensorType,
- resetLockoutRequiresHardwareAuthToken, maxTemplatesAllowed);
+
+ mSensorProperties = new FingerprintSensorProperties(sensorId,
+ Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
+ sensorType, resetLockoutRequiresHardwareAuthToken);
}
- static Fingerprint21 newInstance(@NonNull Context context, int sensorId,
+ public static Fingerprint21 newInstance(@NonNull Context context, int sensorId, int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
final Handler handler = new Handler(Looper.getMainLooper());
final BiometricScheduler scheduler =
new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
final HalResultController controller = new HalResultController(context, handler, scheduler);
- return new Fingerprint21(context, scheduler, handler, sensorId, lockoutResetDispatcher,
- controller);
+ return new Fingerprint21(context, scheduler, handler, sensorId, strength,
+ lockoutResetDispatcher, controller);
}
@Override
@@ -429,7 +435,7 @@
@Nullable IUdfpsOverlayController getUdfpsOverlayController() {
return mUdfpsOverlayController;
}
- @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+ @LockoutTracker.LockoutMode public int getLockoutModeForUser(int userId) {
return mLockoutTracker.getLockoutModeForUser(userId);
}
@@ -475,7 +481,7 @@
});
}
- void scheduleResetLockout(int userId) {
+ public void scheduleResetLockout(int userId) {
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
@@ -483,7 +489,7 @@
});
}
- void scheduleGenerateChallenge(@NonNull IBinder token,
+ public void scheduleGenerateChallenge(@NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
final FingerprintGenerateChallengeClient client =
@@ -494,7 +500,7 @@
});
}
- void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String opPackageName) {
+ public void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String opPackageName) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
@@ -502,7 +508,7 @@
});
}
- void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId,
+ public void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@Nullable Surface surface) {
mHandler.post(() -> {
@@ -525,13 +531,13 @@
});
}
- void cancelEnrollment(@NonNull IBinder token) {
+ public void cancelEnrollment(@NonNull IBinder token) {
mHandler.post(() -> {
mScheduler.cancelEnrollment(token);
});
}
- void scheduleFingerDetect(@NonNull IBinder token, int userId,
+ public void scheduleFingerDetect(@NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
@Nullable Surface surface, int statsClient) {
mHandler.post(() -> {
@@ -546,9 +552,10 @@
});
}
- void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie,
- @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
- boolean restricted, int statsClient, boolean isKeyguard) {
+ public void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId,
+ int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @NonNull String opPackageName, boolean restricted, int statsClient,
+ boolean isKeyguard) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -562,20 +569,21 @@
});
}
- void startPreparedClient(int cookie) {
+ public void startPreparedClient(int cookie) {
mHandler.post(() -> {
mScheduler.startPreparedClient(cookie);
});
}
- void cancelAuthentication(@NonNull IBinder token) {
+ public void cancelAuthentication(@NonNull IBinder token) {
mHandler.post(() -> {
mScheduler.cancelAuthentication(token);
});
}
- void scheduleRemove(@NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver,
- int fingerId, int userId, @NonNull String opPackageName) {
+ public void scheduleRemove(@NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+ @NonNull String opPackageName) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -600,30 +608,30 @@
});
}
- boolean isHardwareDetected() {
+ public boolean isHardwareDetected() {
final IBiometricsFingerprint daemon = getDaemon();
return daemon != null;
}
- @NonNull FingerprintSensorProperties getFingerprintSensorProperties() {
+ @NonNull public FingerprintSensorProperties getFingerprintSensorProperties() {
return mSensorProperties;
}
- void rename(int fingerId, int userId, String name) {
+ public void rename(int fingerId, int userId, String name) {
mHandler.post(() -> {
FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name);
});
}
- List<Fingerprint> getEnrolledFingerprints(int userId) {
+ public List<Fingerprint> getEnrolledFingerprints(int userId) {
return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId);
}
- long getAuthenticatorId(int userId) {
+ public long getAuthenticatorId(int userId) {
return mAuthenticatorIds.get(userId);
}
- void onFingerDown(int x, int y, float minor, float major) {
+ public void onFingerDown(int x, int y, float minor, float major) {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onFingerDown received during client: " + client);
@@ -633,7 +641,7 @@
udfps.onFingerDown(x, y, minor, major);
}
- void onFingerUp() {
+ public void onFingerUp() {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onFingerDown received during client: " + client);
@@ -643,11 +651,11 @@
udfps.onFingerUp();
}
- void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
mUdfpsOverlayController = controller;
}
- void dumpProto(FileDescriptor fd) {
+ public void dumpProto(FileDescriptor fd) {
PerformanceTracker tracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
@@ -687,7 +695,7 @@
tracker.clear();
}
- void dumpInternal(@NonNull PrintWriter pw) {
+ public void dumpInternal(@NonNull PrintWriter pw) {
PerformanceTracker performanceTracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
similarity index 96%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 7cab2b8..6d8f241 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.trust.TrustManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
@@ -36,11 +37,13 @@
import android.util.SparseBooleanArray;
import com.android.internal.R;
+import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.AuthenticationConsumer;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import java.util.ArrayList;
import java.util.Random;
@@ -265,7 +268,8 @@
}
}
- static Fingerprint21UdfpsMock newInstance(@NonNull Context context, int sensorId,
+ public static Fingerprint21UdfpsMock newInstance(@NonNull Context context, int sensorId,
+ @BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
Slog.d(TAG, "Creating Fingerprint23Mock!");
@@ -275,7 +279,7 @@
new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
final MockHalResultController controller =
new MockHalResultController(context, handler, scheduler);
- return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId,
+ return new Fingerprint21UdfpsMock(context, scheduler, handler, sensorId, strength,
lockoutResetDispatcher, controller);
}
@@ -401,9 +405,10 @@
private Fingerprint21UdfpsMock(@NonNull Context context,
@NonNull TestableBiometricScheduler scheduler,
@NonNull Handler handler, int sensorId,
+ @BiometricManager.Authenticators.Types int strength,
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@NonNull MockHalResultController controller) {
- super(context, scheduler, handler, sensorId, lockoutResetDispatcher, controller);
+ super(context, scheduler, handler, sensorId, strength, lockoutResetDispatcher, controller);
mScheduler = scheduler;
mScheduler.init(this);
mHandler = handler;
@@ -412,8 +417,9 @@
final int maxTemplatesAllowed = mContext.getResources()
.getInteger(R.integer.config_fingerprintMaxTemplatesPerUser);
mSensorProperties = new FingerprintSensorProperties(sensorId,
- FingerprintSensorProperties.TYPE_UDFPS, resetLockoutRequiresHardwareAuthToken,
- maxTemplatesAllowed);
+ Utils.authenticatorStrengthToPropertyStrength(strength), maxTemplatesAllowed,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ resetLockoutRequiresHardwareAuthToken);
mMockHalResultController = controller;
mUserHasTrust = new SparseBooleanArray();
mTrustManager = context.getSystemService(TrustManager.class);
@@ -445,12 +451,12 @@
@Override
@NonNull
- FingerprintSensorProperties getFingerprintSensorProperties() {
+ public FingerprintSensorProperties getFingerprintSensorProperties() {
return mSensorProperties;
}
@Override
- void onFingerDown(int x, int y, float minor, float major) {
+ public void onFingerDown(int x, int y, float minor, float major) {
mHandler.post(() -> {
Slog.d(TAG, "onFingerDown");
final AuthenticationConsumer lastAuthenticatedConsumer =
@@ -497,7 +503,7 @@
}
@Override
- void onFingerUp() {
+ public void onFingerUp() {
mHandler.post(() -> {
Slog.d(TAG, "onFingerUp");
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
similarity index 98%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 99d348a..8087e15 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
similarity index 98%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 8652ee4..5865617 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
similarity index 98%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index d5db6e4..1b9fae9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
similarity index 96%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
index 8fb8c99..abaaac5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
similarity index 97%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
index 571d2b8..e061112 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
similarity index 97%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
index 834bf42..5fd1d1e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
similarity index 97%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
index 9f54563..4bbb7ef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
similarity index 96%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
index 882660e..8f58cae 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
similarity index 98%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index c1c3593..00e2413 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.NonNull;
import android.content.Context;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
similarity index 98%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/LockoutFrameworkImpl.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
index e7b2ae7..4fc1545 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/LockoutFrameworkImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
similarity index 93%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
index e0806ff..74cae02 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Udfps.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
/**
* Interface for under-display fingerprint sensors.
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
similarity index 97%
rename from services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
rename to services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
index 5e521d2..c71ecbf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.biometrics.sensors.fingerprint;
+package com.android.server.biometrics.sensors.fingerprint.hidl;
import android.annotation.Nullable;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 1f0066a..01fa9e7 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -367,6 +367,13 @@
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
+ // Ignore the case when the network disconnects immediately after stop() has been
+ // called and the keepalive code is waiting for the response from the modem. This
+ // might happen when the caller listens for a lower-layer network disconnect
+ // callback and stop the keepalive at that time. But the stop() races with the
+ // stop() generated in ConnectivityService network disconnection code path.
+ if (mStartedState == STOPPING && reason == ERROR_INVALID_NETWORK) return;
+
// Store the reason of stopping, and report it after the keepalive is fully stopped.
if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
throw new IllegalStateException("Unexpected stop reason: " + mStopReason);
@@ -380,9 +387,6 @@
// e.g. invalid parameter.
cleanupStoppedKeepalive(mNai, mSlot);
break;
- case STOPPING:
- // Keepalive is already in stopping state, ignore.
- return;
default:
mStartedState = STOPPING;
switch (mType) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 0b4f31d..28bd97e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -117,14 +117,15 @@
// TODO: Validate more than length for the following messages.
// Messages for the One Touch Record.
- FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
addValidationInfo(Constants.MESSAGE_RECORD_ON,
new VariableLengthValidator(1, 8), DEST_DIRECT);
- addValidationInfo(Constants.MESSAGE_RECORD_STATUS, oneByteValidator, DEST_DIRECT);
+ addValidationInfo(Constants.MESSAGE_RECORD_STATUS,
+ new RecordStatusInfoValidator(), DEST_DIRECT);
// TODO: Handle messages for the Timer Programming.
// Messages for the System Information.
+ FixedLengthValidator oneByteValidator = new FixedLengthValidator(1);
addValidationInfo(Constants.MESSAGE_CEC_VERSION, oneByteValidator, DEST_DIRECT);
addValidationInfo(Constants.MESSAGE_SET_MENU_LANGUAGE,
new FixedLengthValidator(3), DEST_BROADCAST);
@@ -339,4 +340,23 @@
isValidPhysicalAddress(params, 0) && isValidPhysicalAddress(params, 2));
}
}
+
+ /**
+ * Check if the given record status message parameter is valid.
+ * A valid parameter should lie within the range description of Record Status Info defined in
+ * CEC 1.4 Specification : Operand Descriptions (Section 17)
+ */
+ private class RecordStatusInfoValidator implements ParameterValidator {
+ @Override
+ public int isValid(byte[] params) {
+ if (params.length < 1) {
+ return ERROR_PARAMETER_SHORT;
+ }
+ return toErrorCode(isWithinRange(params[0], 0x01, 0x07)
+ || isWithinRange(params[0], 0x09, 0x0E)
+ || isWithinRange(params[0], 0x10, 0x17)
+ || isWithinRange(params[0], 0x1A, 0x1B)
+ || params[0] == 0x1F);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 6c14b2c..7c98c6c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -219,10 +219,10 @@
int deviceId, int sourceMask, int sw);
private static native boolean nativeHasKeys(long ptr,
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
- private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel);
- private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
- int displayId, boolean isGestureMonitor);
- private static native void nativeUnregisterInputChannel(long ptr, IBinder connectionToken);
+ private static native InputChannel nativeCreateInputChannel(long ptr, String name);
+ private static native InputChannel nativeCreateInputMonitor(long ptr, int displayId,
+ boolean isGestureMonitor, String name);
+ private static native void nativeRemoveInputChannel(long ptr, IBinder connectionToken);
private static native void nativePilferPointers(long ptr, IBinder token);
private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode);
@@ -522,10 +522,8 @@
throw new IllegalArgumentException("displayId must >= 0.");
}
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
- nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, false /*isGestureMonitor*/);
- inputChannels[0].dispose(); // don't need to retain the Java object reference
- return inputChannels[1];
+ return nativeCreateInputMonitor(mPtr, displayId, false /* isGestureMonitor */,
+ inputChannelName);
}
/**
@@ -552,38 +550,32 @@
final long ident = Binder.clearCallingIdentity();
try {
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
- InputMonitorHost host = new InputMonitorHost(inputChannels[0]);
- nativeRegisterInputMonitor(
- mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/);
+ InputChannel inputChannel = nativeCreateInputMonitor(
+ mPtr, displayId, true /*isGestureMonitor*/, inputChannelName);
+ InputMonitorHost host = new InputMonitorHost(inputChannel.getToken());
synchronized (mGestureMonitorPidsLock) {
- mGestureMonitorPidsByToken.put(inputChannels[1].getToken(), pid);
+ mGestureMonitorPidsByToken.put(inputChannel.getToken(), pid);
}
- return new InputMonitor(inputChannels[1], host);
+ return new InputMonitor(inputChannel, host);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
- * Registers an input channel so that it can be used as an input event target. The channel is
- * registered with a generated token.
+ * Creates an input channel to be used as an input event target.
*
- * @param inputChannel The input channel to register.
+ * @param name The name of this input channel
*/
- public void registerInputChannel(InputChannel inputChannel) {
- if (inputChannel == null) {
- throw new IllegalArgumentException("inputChannel must not be null.");
- }
-
- nativeRegisterInputChannel(mPtr, inputChannel);
+ public InputChannel createInputChannel(String name) {
+ return nativeCreateInputChannel(mPtr, name);
}
/**
- * Unregisters an input channel.
+ * Removes an input channel.
* @param connectionToken The input channel to unregister.
*/
- public void unregisterInputChannel(IBinder connectionToken) {
+ public void removeInputChannel(IBinder connectionToken) {
if (connectionToken == null) {
throw new IllegalArgumentException("connectionToken must not be null.");
}
@@ -591,7 +583,7 @@
mGestureMonitorPidsByToken.remove(connectionToken);
}
- nativeUnregisterInputChannel(mPtr, connectionToken);
+ nativeRemoveInputChannel(mPtr, connectionToken);
}
/**
@@ -2442,21 +2434,20 @@
* Interface for the system to handle request from InputMonitors.
*/
private final class InputMonitorHost extends IInputMonitorHost.Stub {
- private final InputChannel mInputChannel;
+ private final IBinder mToken;
- InputMonitorHost(InputChannel channel) {
- mInputChannel = channel;
+ InputMonitorHost(IBinder token) {
+ mToken = token;
}
@Override
public void pilferPointers() {
- nativePilferPointers(mPtr, mInputChannel.getToken());
+ nativePilferPointers(mPtr, mToken);
}
@Override
public void dispose() {
- nativeUnregisterInputChannel(mPtr, mInputChannel.getToken());
- mInputChannel.dispose();
+ nativeRemoveInputChannel(mPtr, mToken);
}
}
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
index 92dabe3..e28d73e 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -29,6 +29,7 @@
import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
+import java.time.Duration;
import java.util.Objects;
/**
@@ -142,14 +143,13 @@
}
@Override
- void onEnable() {
+ void onEnable(@NonNull Duration initializationTimeout) {
// Set a request on the proxy - it will be sent immediately if the service is bound,
// or will be sent as soon as the service becomes bound.
- // TODO(b/152744911): Decide whether to send a timeout so the provider knows how long
- // it has to generate the first event before it could be bypassed.
LocationTimeZoneProviderRequest request =
new LocationTimeZoneProviderRequest.Builder()
.setReportLocationTimeZone(true)
+ .setInitializationTimeoutMillis(initializationTimeout.toMillis())
.build();
mProxy.setRequest(request);
}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
index 2e2481c..b1e3306 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
@@ -22,6 +22,7 @@
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.TimeZoneDetectorInternal;
+import java.time.Duration;
import java.util.Objects;
/**
@@ -30,6 +31,10 @@
*/
class ControllerEnvironmentImpl extends LocationTimeZoneProviderController.Environment {
+ private static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
+ private static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
+ private static final Duration PROVIDER_UNCERTAINTY_DELAY = Duration.ofMinutes(5);
+
@NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
@NonNull private final LocationTimeZoneProviderController mController;
@@ -45,7 +50,26 @@
}
@Override
+ @NonNull
ConfigurationInternal getCurrentUserConfigurationInternal() {
return mTimeZoneDetectorInternal.getCurrentUserConfigurationInternal();
}
+
+ @Override
+ @NonNull
+ Duration getProviderInitializationTimeout() {
+ return PROVIDER_INITIALIZATION_TIMEOUT;
+ }
+
+ @Override
+ @NonNull
+ Duration getProviderInitializationTimeoutFuzz() {
+ return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
+ }
+
+ @Override
+ @NonNull
+ Duration getUncertaintyDelay() {
+ return PROVIDER_UNCERTAINTY_DELAY;
+ }
}
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
index e31cfc4..bedaeda 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
@@ -33,7 +33,6 @@
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
@@ -50,9 +49,6 @@
*/
class ControllerImpl extends LocationTimeZoneProviderController {
- @VisibleForTesting
- static final Duration UNCERTAINTY_DELAY = Duration.ofMinutes(5);
-
@NonNull private final LocationTimeZoneProvider mProvider;
@NonNull private final SingleRunnableQueue mDelayedSuggestionQueue;
@@ -140,7 +136,8 @@
switch (providerState.stateEnum) {
case PROVIDER_STATE_DISABLED: {
debugLog("Enabling " + mProvider);
- mProvider.enable(configuration);
+ mProvider.enable(
+ configuration, mEnvironment.getProviderInitializationTimeout());
break;
}
case PROVIDER_STATE_ENABLED: {
@@ -183,13 +180,16 @@
if (isProviderEnabled) {
if (!providerWasEnabled) {
// When a provider has first been enabled, we allow it some time for it to
- // initialize.
+ // initialize before sending its first event.
+ Duration initializationTimeout = mEnvironment.getProviderInitializationTimeout()
+ .plus(mEnvironment.getProviderInitializationTimeoutFuzz());
// This sets up an empty suggestion to trigger if no explicit "certain" or
- // "uncertain" suggestion preempts it within UNCERTAINTY_DELAY. If, for some reason,
- // the provider does provide any events then this scheduled suggestion will ensure
- // the controller makes at least an uncertain suggestion.
- suggestDelayed(createEmptySuggestion(
- "No event received in delay=" + UNCERTAINTY_DELAY), UNCERTAINTY_DELAY);
+ // "uncertain" suggestion preempts it within initializationTimeout. If, for some
+ // reason, the provider does not produce any events then this scheduled suggestion
+ // will ensure the controller makes at least an "uncertain" suggestion.
+ suggestDelayed(createEmptySuggestion("No event received from provider in"
+ + " initializationTimeout=" + initializationTimeout),
+ initializationTimeout);
}
} else {
// Clear any queued suggestions.
@@ -199,7 +199,8 @@
// made, then a new "uncertain" suggestion must be made to indicate the provider no
// longer has an opinion and will not be sending updates.
if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
- suggestImmediate(createEmptySuggestion(""));
+ suggestImmediate(createEmptySuggestion(
+ "Provider disabled, clearing previous suggestion"));
}
}
}
@@ -309,21 +310,23 @@
*
* <p>Providers are expected to report their uncertainty as soon as they become uncertain, as
* this enables the most flexibility for the controller to enable other providers when there are
- * multiple ones. The controller is therefore responsible for deciding when to make a
+ * multiple ones available. The controller is therefore responsible for deciding when to make a
* "uncertain" suggestion.
*
* <p>This method schedules an "uncertain" suggestion (if one isn't already scheduled) to be
* made later if nothing else preempts it. It can be preempted if the provider becomes certain
* (or does anything else that calls {@link #suggestImmediate(GeolocationTimeZoneSuggestion)})
- * within UNCERTAINTY_DELAY. Preemption causes the scheduled "uncertain" event to be cancelled.
- * If the provider repeatedly sends uncertainty events within UNCERTAINTY_DELAY, those events
- * are effectively ignored (i.e. the timer is not reset each time).
+ * within {@link Environment#getUncertaintyDelay()}. Preemption causes the scheduled
+ * "uncertain" event to be cancelled. If the provider repeatedly sends uncertainty events within
+ * the uncertainty delay period, those events are effectively ignored (i.e. the timer is not
+ * reset each time).
*/
private void scheduleUncertainSuggestionIfNeeded(@Nullable LocationTimeZoneEvent event) {
if (mPendingSuggestion == null || mPendingSuggestion.getZoneIds() != null) {
GeolocationTimeZoneSuggestion suggestion = createEmptySuggestion(
"provider=" + mProvider + " became uncertain, event=" + event);
- suggestDelayed(suggestion, UNCERTAINTY_DELAY);
+ // Only send the empty suggestion after the uncertainty delay.
+ suggestDelayed(suggestion, mEnvironment.getUncertaintyDelay());
}
}
@@ -334,6 +337,11 @@
ipw.increaseIndent(); // level 1
ipw.println("mCurrentUserConfiguration=" + mCurrentUserConfiguration);
+ ipw.println("providerInitializationTimeout="
+ + mEnvironment.getProviderInitializationTimeout());
+ ipw.println("providerInitializationTimeoutFuzz="
+ + mEnvironment.getProviderInitializationTimeoutFuzz());
+ ipw.println("uncertaintyDelay=" + mEnvironment.getUncertaintyDelay());
ipw.println("mPendingSuggestion=" + mPendingSuggestion);
ipw.println("mLastSuggestion=" + mLastSuggestion);
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
index 3743779..abfa580 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
@@ -37,6 +37,7 @@
import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.ReferenceWithHistory;
+import java.time.Duration;
import java.util.Objects;
/**
@@ -368,7 +369,8 @@
* #getCurrentState()} is at {@link ProviderState#PROVIDER_STATE_DISABLED}. This method must be
* called using the handler thread from the {@link ThreadingDomain}.
*/
- final void enable(@NonNull ConfigurationInternal currentUserConfiguration) {
+ final void enable(@NonNull ConfigurationInternal currentUserConfiguration,
+ @NonNull Duration initializationTimeout) {
mThreadingDomain.assertCurrentThread();
synchronized (mSharedLock) {
@@ -378,14 +380,14 @@
ProviderState newState = currentState.newState(
PROVIDER_STATE_ENABLED, null, currentUserConfiguration, "enable() called");
setCurrentState(newState, false);
- onEnable();
+ onEnable(initializationTimeout);
}
}
/**
* Implemented by subclasses to do work during {@link #enable}.
*/
- abstract void onEnable();
+ abstract void onEnable(@NonNull Duration initializationTimeout);
/**
* Disables the provider. It is an error* to call this method except when the {@link
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
index 2f75c43..88f0f00 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
@@ -24,6 +24,7 @@
import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import java.time.Duration;
import java.util.Objects;
/**
@@ -100,6 +101,24 @@
/** Returns the {@link ConfigurationInternal} for the current user of the device. */
abstract ConfigurationInternal getCurrentUserConfigurationInternal();
+
+ /**
+ * Returns the value passed to LocationTimeZoneProviders informing them of how long they
+ * have to return their first time zone suggestion.
+ */
+ abstract Duration getProviderInitializationTimeout();
+
+ /**
+ * Returns the extra time granted on top of {@link #getProviderInitializationTimeout()} to
+ * allow for slop like communication delays.
+ */
+ abstract Duration getProviderInitializationTimeoutFuzz();
+
+ /**
+ * Returns the delay allowed after receiving uncertainty from a provider before it should be
+ * passed on.
+ */
+ abstract Duration getUncertaintyDelay();
}
/**
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
index 79e2b97..53afaf0 100644
--- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
@@ -23,6 +23,8 @@
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import java.time.Duration;
+
/**
* A {@link LocationTimeZoneProvider} that provides minimal responses needed for the {@link
* LocationTimeZoneProviderController} to operate correctly when there is no "real" provider
@@ -55,7 +57,7 @@
}
@Override
- void onEnable() {
+ void onEnable(@NonNull Duration initializationTimeout) {
// Report a failure (asynchronously using the mThreadingDomain thread to avoid recursion).
mThreadingDomain.post(()-> {
// Enter the perm-failed state.
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 68961d9..7e53e6f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2616,7 +2616,6 @@
final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
getGateKeeperService(), credentialHash, credential, userId);
- onAuthTokenKnownForUser(userId, auth);
if (auth == null) {
Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token");
return null;
@@ -2639,6 +2638,7 @@
}
fixateNewestUserKeyAuth(userId);
setSyntheticPasswordHandleLocked(handle, userId);
+ onAuthTokenKnownForUser(userId, auth);
return auth;
}
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index b9997e0..0a4d17f 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -259,7 +259,7 @@
return "";
}
return String.join(COMPONENT_NAME_USER_ID_DELIM,
- mComponentName.toString(),
+ mComponentName.flattenToString(),
String.valueOf(mUserId),
String.valueOf(mComponentType));
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7401403..79882da 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -309,6 +309,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -1528,10 +1529,10 @@
cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, null, 0, 0,
!queryRestart, changeUserId, reason, null);
}
- } else if (hideNotifications) {
- hideNotificationsForPackages(pkgList);
- } else if (unhideNotifications) {
- unhideNotificationsForPackages(pkgList);
+ } else if (hideNotifications && uidList != null && (uidList.length > 0)) {
+ hideNotificationsForPackages(pkgList, uidList);
+ } else if (unhideNotifications && uidList != null && (uidList.length > 0)) {
+ unhideNotificationsForPackages(pkgList, uidList);
}
}
@@ -2076,6 +2077,68 @@
mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource(
com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos));
mStatsManager = statsManager;
+
+ // register for various Intents.
+ // If this is called within a test, make sure to unregister the intent receivers by
+ // calling onDestroy()
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_USER_PRESENT);
+ filter.addAction(Intent.ACTION_USER_STOPPED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_USER_ADDED);
+ filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+ getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
+
+ IntentFilter pkgFilter = new IntentFilter();
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+ pkgFilter.addDataScheme("package");
+ getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
+ null);
+
+ IntentFilter suspendedPkgFilter = new IntentFilter();
+ suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+ suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
+ suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED);
+ getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
+ suspendedPkgFilter, null, null);
+
+ IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
+ null);
+
+ IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
+ timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
+ getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
+
+ IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
+ getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
+
+ IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
+ }
+
+ /**
+ * Cleanup broadcast receivers change listeners.
+ */
+ public void onDestroy() {
+ getContext().unregisterReceiver(mIntentReceiver);
+ getContext().unregisterReceiver(mPackageIntentReceiver);
+ getContext().unregisterReceiver(mNotificationTimeoutReceiver);
+ getContext().unregisterReceiver(mRestoreReceiver);
+ getContext().unregisterReceiver(mLocaleChangeReceiver);
+
+ if (mDeviceConfigChangedListener != null) {
+ DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
+ }
}
protected String[] getStringArrayResource(int key) {
@@ -2126,51 +2189,6 @@
Context.STATS_MANAGER),
getContext().getSystemService(TelephonyManager.class));
- // register for various Intents
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
- filter.addAction(Intent.ACTION_USER_PRESENT);
- filter.addAction(Intent.ACTION_USER_STOPPED);
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- filter.addAction(Intent.ACTION_USER_ADDED);
- filter.addAction(Intent.ACTION_USER_REMOVED);
- filter.addAction(Intent.ACTION_USER_UNLOCKED);
- filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
- getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null);
-
- IntentFilter pkgFilter = new IntentFilter();
- pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
- pkgFilter.addDataScheme("package");
- getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
- null);
-
- IntentFilter suspendedPkgFilter = new IntentFilter();
- suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
- suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
- suspendedPkgFilter.addAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED);
- getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
- suspendedPkgFilter, null, null);
-
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
- null);
-
- IntentFilter timeoutFilter = new IntentFilter(ACTION_NOTIFICATION_TIMEOUT);
- timeoutFilter.addDataScheme(SCHEME_TIMEOUT);
- getContext().registerReceiver(mNotificationTimeoutReceiver, timeoutFilter);
-
- IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
- getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
-
- IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
- getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
-
publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
publishLocalService(NotificationManagerInternal.class, mInternalService);
@@ -2193,12 +2211,6 @@
mDeviceConfigChangedListener);
}
- void unregisterDeviceConfigChange() {
- if (mDeviceConfigChangedListener != null) {
- DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
- }
- }
-
private void registerNotificationPreferencesPullers() {
mPullAtomCallback = new StatsPullAtomCallbackImpl();
mStatsManager.setPullAtomCallback(
@@ -8007,7 +8019,7 @@
int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
- ArrayList<NotificationRecord> canceledNotifications = null;
+ Set<String> childNotifications = null;
for (int i = notificationList.size() - 1; i >= 0; --i) {
NotificationRecord r = notificationList.get(i);
if (includeCurrentProfiles) {
@@ -8030,20 +8042,30 @@
if (channelId != null && !channelId.equals(r.getChannel().getId())) {
continue;
}
- if (canceledNotifications == null) {
- canceledNotifications = new ArrayList<>();
+ if (r.getSbn().isGroup() && r.getNotification().isGroupChild()) {
+ if (childNotifications == null) {
+ childNotifications = new HashSet<>();
+ }
+ childNotifications.add(r.getKey());
+ continue;
}
notificationList.remove(i);
mNotificationsByKey.remove(r.getKey());
r.recordDismissalSentiment(NotificationStats.DISMISS_SENTIMENT_NEUTRAL);
- canceledNotifications.add(r);
cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
}
- if (canceledNotifications != null) {
- final int M = canceledNotifications.size();
- for (int i = 0; i < M; i++) {
- cancelGroupChildrenLocked(canceledNotifications.get(i), callingUid, callingPid,
- listenerName, false /* sendDelete */, flagChecker, reason);
+ if (childNotifications != null) {
+ final int M = notificationList.size();
+ for (int i = M - 1; i >= 0; i--) {
+ NotificationRecord r = notificationList.get(i);
+ if (childNotifications.contains(r.getKey())) {
+ // dismiss conditions were checked in the first loop and so don't need to be
+ // checked again
+ notificationList.remove(i);
+ mNotificationsByKey.remove(r.getKey());
+ r.recordDismissalSentiment(NotificationStats.DISMISS_SENTIMENT_NEUTRAL);
+ cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
+ }
}
updateLightsLocked();
}
@@ -8339,15 +8361,16 @@
return -1;
}
- @VisibleForTesting
- protected void hideNotificationsForPackages(String[] pkgs) {
+ private void hideNotificationsForPackages(@NonNull String[] pkgs, @NonNull int[] uidList) {
synchronized (mNotificationLock) {
+ Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet());
List<String> pkgList = Arrays.asList(pkgs);
List<NotificationRecord> changedNotifications = new ArrayList<>();
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- if (pkgList.contains(rec.getSbn().getPackageName())) {
+ if (pkgList.contains(rec.getSbn().getPackageName())
+ && uidSet.contains(rec.getUid())) {
rec.setHidden(true);
changedNotifications.add(rec);
}
@@ -8357,15 +8380,17 @@
}
}
- @VisibleForTesting
- protected void unhideNotificationsForPackages(String[] pkgs) {
+ private void unhideNotificationsForPackages(@NonNull String[] pkgs,
+ @NonNull int[] uidList) {
synchronized (mNotificationLock) {
+ Set<Integer> uidSet = Arrays.stream(uidList).boxed().collect(Collectors.toSet());
List<String> pkgList = Arrays.asList(pkgs);
List<NotificationRecord> changedNotifications = new ArrayList<>();
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- if (pkgList.contains(rec.getSbn().getPackageName())) {
+ if (pkgList.contains(rec.getSbn().getPackageName())
+ && uidSet.contains(rec.getUid())) {
rec.setHidden(false);
changedNotifications.add(rec);
}
@@ -9939,38 +9964,6 @@
return CollectionUtils.firstOrNull(allowedComponents);
}
- @VisibleForTesting
- protected void simulatePackageSuspendBroadcast(boolean suspend, String pkg) {
- checkCallerIsSystemOrShell();
- // only use for testing: mimic receive broadcast that package is (un)suspended
- // but does not actually (un)suspend the package
- final Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
- new String[]{pkg});
-
- final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
- : Intent.ACTION_PACKAGES_UNSUSPENDED;
- final Intent intent = new Intent(action);
- intent.putExtras(extras);
-
- mPackageIntentReceiver.onReceive(getContext(), intent);
- }
-
- @VisibleForTesting
- protected void simulatePackageDistractionBroadcast(int flag, String[] pkgs) {
- checkCallerIsSystemOrShell();
- // only use for testing: mimic receive broadcast that package is (un)distracting
- // but does not actually register that info with packagemanager
- final Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs);
- extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag);
-
- final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED);
- intent.putExtras(extras);
-
- mPackageIntentReceiver.onReceive(getContext(), intent);
- }
-
/**
* Wrapper for a StatusBarNotification object that allows transfer across a oneway
* binder without sending large amounts of data over a oneway transaction.
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index da7864b..927dc25 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -66,8 +66,6 @@
+ " set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]"
+ " allow_dnd PACKAGE [user_id (current user if not specified)]\n"
+ " disallow_dnd PACKAGE [user_id (current user if not specified)]\n"
- + " suspend_package PACKAGE\n"
- + " unsuspend_package PACKAGE\n"
+ " reset_assistant_user_set [user_id (current user if not specified)]\n"
+ " get_approved_assistant [user_id (current user if not specified)]\n"
+ " post [--help | flags] TAG TEXT\n"
@@ -258,25 +256,6 @@
mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, false);
}
break;
- case "suspend_package": {
- // only use for testing
- mDirectService.simulatePackageSuspendBroadcast(true, getNextArgRequired());
- }
- break;
- case "unsuspend_package": {
- // only use for testing
- mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired());
- }
- break;
- case "distract_package": {
- // only use for testing
- // Flag values are in
- // {@link android.content.pm.PackageManager.DistractionRestriction}.
- mDirectService.simulatePackageDistractionBroadcast(
- Integer.parseInt(getNextArgRequired()),
- getNextArgRequired().split(","));
- break;
- }
case "reset_assistant_user_set": {
int userId = ActivityManager.getCurrentUser();
if (peekNextArg() != null) {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 5bbe490..4c1b700 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -75,6 +75,7 @@
import android.util.Pair;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
@@ -143,6 +144,9 @@
private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+ @GuardedBy("mListeners")
+ private boolean mIsWatchingPackageBroadcasts = false;
+
private final ShortcutChangeHandler mShortcutChangeHandler;
private final Handler mCallbackHandler;
@@ -281,7 +285,10 @@
* Register a receiver to watch for package broadcasts
*/
private void startWatchingPackageBroadcasts() {
- mPackageMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
+ if (!mIsWatchingPackageBroadcasts) {
+ mPackageMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
+ mIsWatchingPackageBroadcasts = true;
+ }
}
/**
@@ -291,7 +298,10 @@
if (DEBUG) {
Log.d(TAG, "Stopped watching for packages");
}
- mPackageMonitor.unregister();
+ if (mIsWatchingPackageBroadcasts) {
+ mPackageMonitor.unregister();
+ mIsWatchingPackageBroadcasts = false;
+ }
}
void checkCallbackCount() {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index f52db5f..31ee597 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -618,6 +618,13 @@
}
}
+ if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
+ && !isCalledBySystemOrShell(callingUid)
+ && (mPm.getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ throw new SecurityException(
+ "Only system apps could use the PackageManager.INSTALL_INSTANT_APP flag.");
+ }
+
if (params.isStaged && !isCalledBySystemOrShell(callingUid)) {
if (mBypassNextStagedInstallerCheck) {
mBypassNextStagedInstallerCheck = false;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 2a189c0..60737bf 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -169,6 +169,7 @@
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
public class PackageInstallerSession extends IPackageInstallerSession.Stub {
private static final String TAG = "PackageInstallerSession";
@@ -689,7 +690,7 @@
stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
if (isDataLoaderInstallation()) {
- if (isApexInstallation()) {
+ if (isApexSession()) {
throw new IllegalArgumentException(
"DataLoader installation of APEX modules is not allowed.");
}
@@ -1576,7 +1577,7 @@
return false;
}
- if (isApexInstallation()) {
+ if (isApexSession()) {
validateApexInstallLocked();
} else {
validateApkInstallLocked();
@@ -1733,7 +1734,7 @@
try {
sealLocked();
- if (isApexInstallation()) {
+ if (isApexSession()) {
// APEX installations rely on certain fields to be populated after reboot.
// E.g. mPackageName.
validateApexInstallLocked();
@@ -1809,7 +1810,7 @@
return;
}
- if (isApexInstallation()) {
+ if (isApexSession()) {
destroyInternal();
dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"APEX packages can only be installed using staged sessions.", null);
@@ -1986,7 +1987,7 @@
// TODO(b/136257624): Some logic in this if block probably belongs in
// makeInstallParams().
- if (!params.isMultiPackage && !isApexInstallation()) {
+ if (!params.isMultiPackage && !isApexSession()) {
Objects.requireNonNull(mPackageName);
Objects.requireNonNull(mSigningDetails);
Objects.requireNonNull(mResolvedBaseFile);
@@ -2215,10 +2216,34 @@
/**
* Returns true if the session is installing an APEX package.
*/
- private boolean isApexInstallation() {
+ boolean isApexSession() {
return (params.installFlags & PackageManager.INSTALL_APEX) != 0;
}
+ private boolean sessionContains(Predicate<PackageInstallerSession> filter) {
+ if (!isMultiPackage()) {
+ return filter.test(this);
+ }
+ final List<PackageInstallerSession> childSessions;
+ synchronized (mLock) {
+ childSessions = getChildSessionsLocked();
+ }
+ for (PackageInstallerSession child: childSessions) {
+ if (filter.test(child)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ boolean containsApexSession() {
+ return sessionContains((s) -> s.isApexSession());
+ }
+
+ boolean containsApkSession() {
+ return sessionContains((s) -> !s.isApexSession());
+ }
+
/**
* Validate apex install.
* <p>
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 697c31a..b451eaf1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -282,6 +282,7 @@
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
import android.permission.IPermissionManager;
+import android.provider.ContactsContract;
import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
@@ -349,6 +350,7 @@
import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog;
+import com.android.server.compat.CompatChange;
import com.android.server.compat.PlatformCompat;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
@@ -1849,7 +1851,6 @@
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_PACKAGE_LIST);
- mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writePackageListLPr(msg.arg1);
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
@@ -2127,11 +2128,21 @@
final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null;
final String packageName = res.name;
final PackageSetting pkgSetting = succeeded ? getPackageSetting(packageName) : null;
- if (succeeded && pkgSetting == null) {
+ final boolean removedBeforeUpdate = (pkgSetting == null)
+ || (pkgSetting.isSystem() && !pkgSetting.getPathString().equals(res.pkg.getPath()));
+ if (succeeded && removedBeforeUpdate) {
Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+ "could be executed");
res.returnCode = INSTALL_FAILED_PACKAGE_CHANGED;
res.returnMsg = "Package was removed before install could complete.";
+
+ // Remove the update failed package's older resources safely now
+ InstallArgs args = res.removedInfo != null ? res.removedInfo.args : null;
+ if (args != null) {
+ synchronized (mInstallLock) {
+ args.doPostDeleteLI(true);
+ }
+ }
notifyInstallObserver(res, installObserver);
return;
}
@@ -2686,7 +2697,8 @@
(i, pm) ->
new Settings(Environment.getDataDirectory(),
i.getPermissionManagerServiceInternal().getPermissionSettings(),
- RuntimePermissionsPersistence.createInstance(), lock),
+ RuntimePermissionsPersistence.createInstance(),
+ i.getPermissionManagerServiceInternal(), lock),
new Injector.LocalServicesProducer<>(ActivityTaskManagerInternal.class),
new Injector.LocalServicesProducer<>(ActivityManagerInternal.class),
new Injector.LocalServicesProducer<>(DeviceIdleInternal.class),
@@ -2703,39 +2715,43 @@
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest);
t.traceEnd(); // "create package manager"
- injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
- packageName -> {
- synchronized (m.mInstallLock) {
- final AndroidPackage pkg;
- final PackageSetting ps;
- final SharedUserSetting sharedUser;
- final String oldSeInfo;
- synchronized (m.mLock) {
- ps = m.mSettings.getPackageLPr(packageName);
- if (ps == null) {
- Slog.e(TAG, "Failed to find package setting " + packageName);
- return;
- }
- pkg = ps.pkg;
- sharedUser = ps.getSharedUser();
- oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
- }
-
- if (pkg == null) {
- Slog.e(TAG, "Failed to find package " + packageName);
- return;
- }
- final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
- m.mInjector.getCompatibility());
-
- if (!newSeInfo.equals(oldSeInfo)) {
- Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
- + oldSeInfo + " to: " + newSeInfo);
- ps.getPkgState().setOverrideSeInfo(newSeInfo);
- m.prepareAppDataAfterInstallLIF(pkg);
- }
+ final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
+ synchronized (m.mInstallLock) {
+ final AndroidPackage pkg;
+ final PackageSetting ps;
+ final SharedUserSetting sharedUser;
+ final String oldSeInfo;
+ synchronized (m.mLock) {
+ ps = m.mSettings.getPackageLPr(packageName);
+ if (ps == null) {
+ Slog.e(TAG, "Failed to find package setting " + packageName);
+ return;
}
- });
+ pkg = ps.pkg;
+ sharedUser = ps.getSharedUser();
+ oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
+ }
+
+ if (pkg == null) {
+ Slog.e(TAG, "Failed to find package " + packageName);
+ return;
+ }
+ final String newSeInfo = SELinuxMMAC.getSeInfo(pkg, sharedUser,
+ m.mInjector.getCompatibility());
+
+ if (!newSeInfo.equals(oldSeInfo)) {
+ Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
+ + oldSeInfo + " to: " + newSeInfo);
+ ps.getPkgState().setOverrideSeInfo(newSeInfo);
+ m.prepareAppDataAfterInstallLIF(pkg);
+ }
+ }
+ };
+
+ injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
+ selinuxChangeListener);
+ injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_R_CHANGES,
+ selinuxChangeListener);
m.installWhitelistedSystemPackages();
ServiceManager.addService("package", m);
@@ -4486,8 +4502,8 @@
AndroidPackage p = ps.pkg;
if (p != null) {
// Compute GIDs only if requested
- final int[] gids = (flags & PackageManager.GET_GIDS) == 0
- ? EMPTY_INT_ARRAY : mPermissionManager.getPackageGids(ps.name, userId);
+ final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY
+ : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
// Compute granted permissions only if package has requested permissions
final Set<String> permissions = ArrayUtils.isEmpty(p.getRequestedPermissions())
? Collections.emptySet()
@@ -4962,13 +4978,13 @@
}
// TODO: Shouldn't this be checking for package installed state for userId and
// return null?
- return mPermissionManager.getPackageGids(packageName, userId);
+ return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
if (ps != null && ps.isMatch(flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return mPermissionManager.getPackageGids(packageName, userId);
+ return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
}
}
}
@@ -18983,7 +18999,7 @@
}
if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0)
&& !isUpdatedSystemApp(deletedPs)) {
- mPermissionManager.removePermissionsStateTEMP(removedAppId);
+ mPermissionManager.removeAppIdStateTEMP(removedAppId);
}
mPermissionManager.updatePermissions(deletedPs.name, null);
if (deletedPs.sharedUser != null) {
@@ -21242,7 +21258,8 @@
// Prior to enabling the package, we need to decompress the APK(s) to the
// data partition and then replace the version on the system partition.
final AndroidPackage deletedPkg = pkgSetting.pkg;
- final boolean isSystemStub = deletedPkg.isStub()
+ final boolean isSystemStub = (deletedPkg != null)
+ && deletedPkg.isStub()
&& deletedPkg.isSystem();
if (isSystemStub
&& (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
@@ -21854,8 +21871,6 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
- mPermissionManager.writeStateToPackageSettingsTEMP();
-
DumpState dumpState = new DumpState();
boolean fullPreferred = false;
boolean checkin = false;
@@ -23734,7 +23749,6 @@
mDirtyUsers.remove(userId);
mUserNeedsBadging.delete(userId);
mPermissionManager.onUserRemoved(userId);
- mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.removeUserLPw(userId);
mPendingBroadcasts.remove(userId);
mInstantAppRegistry.onUserRemovedLPw(userId);
@@ -25716,6 +25730,32 @@
}
}
+ @Override
+ public void grantImplicitAccess(int recipientUid, String visibleAuthority) {
+ // This API is exposed temporarily to only the contacts provider. (b/158688602)
+ final int callingUid = Binder.getCallingUid();
+ ProviderInfo contactsProvider = resolveContentProviderInternal(
+ ContactsContract.AUTHORITY, 0, UserHandle.USER_SYSTEM);
+ if (contactsProvider == null || contactsProvider.applicationInfo == null
+ || !UserHandle.isSameApp(contactsProvider.applicationInfo.uid, callingUid)) {
+ throw new SecurityException(callingUid + " is not allow to call grantImplicitAccess");
+ }
+ final int userId = UserHandle.getUserId(recipientUid);
+ final long token = Binder.clearCallingIdentity();
+ final ProviderInfo providerInfo;
+ try {
+ providerInfo = resolveContentProvider(visibleAuthority, 0 /*flags*/, userId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ if (providerInfo == null) {
+ return;
+ }
+ int visibleUid = providerInfo.applicationInfo.uid;
+ mPmInternal.grantImplicitAccess(userId, null /*Intent*/, UserHandle.getAppId(recipientUid),
+ visibleUid, false);
+ }
+
boolean canHaveOatDir(String packageName) {
synchronized (mLock) {
AndroidPackage p = mPackages.get(packageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d77683e..afbf7d3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2323,7 +2323,8 @@
private boolean isVendorApp(String pkg) {
try {
- final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+ final PackageInfo info = mInterface.getPackageInfo(
+ pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM);
return info != null && info.applicationInfo.isVendor();
} catch (RemoteException e) {
return false;
@@ -2332,7 +2333,8 @@
private boolean isProductApp(String pkg) {
try {
- final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+ final PackageInfo info = mInterface.getPackageInfo(
+ pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM);
return info != null && info.applicationInfo.isProduct();
} catch (RemoteException e) {
return false;
@@ -2341,7 +2343,8 @@
private boolean isSystemExtApp(String pkg) {
try {
- final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM);
+ final PackageInfo info = mInterface.getPackageInfo(
+ pkg, PackageManager.MATCH_ANY_USER, UserHandle.USER_SYSTEM);
return info != null && info.applicationInfo.isSystemExt();
} catch (RemoteException e) {
return false;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 276f880..855a5ff5 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -28,7 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.AppIdPermissionState;
import com.android.server.pm.pkg.PackageStateUnserialized;
import java.io.File;
@@ -215,7 +215,7 @@
}
@Override
- public PermissionsState getPermissionsState() {
+ public AppIdPermissionState getPermissionsState() {
return (sharedUser != null)
? sharedUser.getPermissionsState()
: super.getPermissionsState();
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index fdd9636..c5fbfba 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -18,6 +18,7 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.Signature;
import android.os.Environment;
@@ -77,9 +78,21 @@
private static final String TARGETSDKVERSION_STR = ":targetSdkVersion=";
/**
- * This change gates apps access to untrusted_app_R-targetSDk SELinux domain. Allows opt-in
+ * Allows opt-in to the latest targetSdkVersion enforced changes without changing target SDK.
+ * Turning this change off for an app targeting the latest SDK is a no-op.
+ *
+ * <p>Has no effect for apps using shared user id.
+ *
+ * TODO(b/143539591): Update description with relevant SELINUX changes this opts in to.
+ */
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ @ChangeId
+ static final long SELINUX_LATEST_CHANGES = 143539591L;
+
+ /**
+ * This change gates apps access to untrusted_app_R-targetSDK SELinux domain. Allows opt-in
* to R targetSdkVersion enforced changes without changing target SDK. Turning this change
- * off for an app targeting R is a no-op.
+ * off for an app targeting S is a no-op.
*
* <p>Has no effect for apps using shared user id.
*
@@ -87,7 +100,7 @@
*/
@EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q)
@ChangeId
- static final long SELINUX_LATEST_CHANGES = 143539591L;
+ static final long SELINUX_R_CHANGES = 168782947L;
// Only initialize sMacPermissions once.
static {
@@ -349,9 +362,11 @@
if ((sharedUserSetting != null) && (sharedUserSetting.packages.size() != 0)) {
return sharedUserSetting.seInfoTargetSdkVersion;
}
- if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES,
- pkg.toAppInfoWithoutState())) {
- return android.os.Build.VERSION_CODES.R;
+ final ApplicationInfo appInfo = pkg.toAppInfoWithoutState();
+ if (compatibility.isChangeEnabledInternal(SELINUX_LATEST_CHANGES, appInfo)) {
+ return android.os.Build.VERSION_CODES.S;
+ } else if (compatibility.isChangeEnabledInternal(SELINUX_R_CHANGES, appInfo)) {
+ return Math.max(android.os.Build.VERSION_CODES.R, pkg.getTargetSdkVersion());
}
return pkg.getTargetSdkVersion();
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 3e2ab05..c1258b1 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -19,23 +19,23 @@
import android.content.pm.ApplicationInfo;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.permission.PermissionsState;
+import com.android.server.pm.permission.AppIdPermissionState;
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public abstract class SettingBase {
int pkgFlags;
int pkgPrivateFlags;
- protected final PermissionsState mPermissionsState;
+ protected final AppIdPermissionState mPermissionsState;
SettingBase(int pkgFlags, int pkgPrivateFlags) {
setFlags(pkgFlags);
setPrivateFlags(pkgPrivateFlags);
- mPermissionsState = new PermissionsState();
+ mPermissionsState = new AppIdPermissionState();
}
SettingBase(SettingBase orig) {
- mPermissionsState = new PermissionsState();
+ mPermissionsState = new AppIdPermissionState();
doCopy(orig);
}
@@ -49,7 +49,7 @@
mPermissionsState.copyFrom(orig.mPermissionsState);
}
- public PermissionsState getPermissionsState() {
+ public AppIdPermissionState getPermissionsState() {
return mPermissionsState;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 9654307..bae36b2a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -79,6 +79,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.IntArray;
import android.util.Log;
import android.util.LogPrinter;
import android.util.Pair;
@@ -106,10 +107,11 @@
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+import com.android.server.pm.permission.AppIdPermissionState;
+import com.android.server.pm.permission.AppIdPermissionState.PermissionState;
import com.android.server.pm.permission.BasePermission;
+import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.PermissionSettings;
-import com.android.server.pm.permission.PermissionsState;
-import com.android.server.pm.permission.PermissionsState.PermissionState;
import com.android.server.utils.TimingsTraceAndSlog;
import libcore.io.IoUtils;
@@ -416,9 +418,12 @@
private final File mSystemDir;
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
+
/** Settings and other information about permissions */
final PermissionSettings mPermissions;
+ private final LegacyPermissionDataProvider mPermissionDataProvider;
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public Settings(Map<String, PackageSetting> pkgSettings) {
mLock = new Object();
@@ -426,6 +431,7 @@
mSystemDir = null;
mPermissions = null;
mRuntimePermissionsPersistence = null;
+ mPermissionDataProvider = null;
mSettingsFilename = null;
mBackupSettingsFilename = null;
mPackageListFilename = null;
@@ -434,12 +440,14 @@
mKernelMappingFilename = null;
}
- Settings(File dataDir, PermissionSettings permission,
- RuntimePermissionsPersistence runtimePermissionsPersistence, Object lock) {
+ Settings(File dataDir, PermissionSettings permissionSettings,
+ RuntimePermissionsPersistence runtimePermissionsPersistence,
+ LegacyPermissionDataProvider permissionDataProvider, Object lock) {
mLock = lock;
- mPermissions = permission;
+ mPermissions = permissionSettings;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
- runtimePermissionsPersistence, mLock);
+ runtimePermissionsPersistence);
+ mPermissionDataProvider = permissionDataProvider;
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
@@ -1239,7 +1247,7 @@
void writeAllRuntimePermissionsLPr() {
for (int userId : UserManagerService.getInstance().getUserIds()) {
- mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
}
}
@@ -2102,7 +2110,7 @@
}
void readInstallPermissionsLPr(XmlPullParser parser,
- PermissionsState permissionsState) throws IOException, XmlPullParserException {
+ AppIdPermissionState permissionsState) throws IOException, XmlPullParserException {
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2131,25 +2139,7 @@
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
- if (granted) {
- if (permissionsState.grantInstallPermission(bp) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
- XmlUtils.skipCurrentTag(parser);
- } else {
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- }
- } else {
- if (permissionsState.revokeInstallPermission(bp) ==
- PermissionsState.PERMISSION_OPERATION_FAILURE) {
- Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
- XmlUtils.skipCurrentTag(parser);
- } else {
- permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- }
- }
+ permissionsState.putInstallPermissionState(new PermissionState(bp, granted, flags));
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
+ parser.getName());
@@ -2158,7 +2148,7 @@
}
}
- void writePermissionsLPr(XmlSerializer serializer, List<PermissionState> permissionStates)
+ void writePermissionsLPr(XmlSerializer serializer, Collection<PermissionState> permissionStates)
throws IOException {
if (permissionStates.isEmpty()) {
return;
@@ -2641,7 +2631,11 @@
}
final boolean isDebug = pkg.pkg.isDebuggable();
- final int[] gids = pkg.getPermissionsState().computeGids(userIds);
+ final IntArray gids = new IntArray();
+ for (final int userId : userIds) {
+ gids.addAll(mPermissionDataProvider.getGidsForUid(UserHandle.getUid(userId,
+ pkg.appId)));
+ }
// Avoid any application that has a space in its path.
if (dataPath.indexOf(' ') >= 0)
@@ -2673,11 +2667,12 @@
sb.append(" ");
sb.append(AndroidPackageUtils.getSeInfo(pkg.pkg, pkg));
sb.append(" ");
- if (gids != null && gids.length > 0) {
- sb.append(gids[0]);
- for (int i = 1; i < gids.length; i++) {
+ final int gidsSize = gids.size();
+ if (gids != null && gids.size() > 0) {
+ sb.append(gids.get(0));
+ for (int i = 1; i < gidsSize; i++) {
sb.append(",");
- sb.append(gids[i]);
+ sb.append(gids.get(i));
}
} else {
sb.append("none");
@@ -4482,8 +4477,9 @@
}
void dumpPackageLPr(PrintWriter pw, String prefix, String checkinTag,
- ArraySet<String> permissionNames, PackageSetting ps, SimpleDateFormat sdf,
- Date date, List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
+ ArraySet<String> permissionNames, PackageSetting ps,
+ AppIdPermissionState permissionsState, SimpleDateFormat sdf, Date date,
+ List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
AndroidPackage pkg = ps.pkg;
if (checkinTag != null) {
pw.print(checkinTag);
@@ -4810,7 +4806,6 @@
}
if (ps.sharedUser == null || permissionNames != null || dumpAll) {
- PermissionsState permissionsState = ps.getPermissionsState();
dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState);
}
@@ -4889,8 +4884,8 @@
}
if (ps.sharedUser == null) {
- PermissionsState permissionsState = ps.getPermissionsState();
- dumpGidsLPr(pw, prefix + " ", permissionsState.computeGids(user.id));
+ dumpGidsLPr(pw, prefix + " ", mPermissionDataProvider.getGidsForUid(
+ UserHandle.getUid(user.id, ps.appId)));
dumpRuntimePermissionsLPr(pw, prefix + " ", permissionNames, permissionsState
.getRuntimePermissionStates(user.id), dumpAll);
}
@@ -4933,8 +4928,10 @@
&& !packageName.equals(ps.name)) {
continue;
}
+ final AppIdPermissionState permissionsState =
+ mPermissionDataProvider.getAppIdPermissionState(ps.appId);
if (permissionNames != null
- && !ps.getPermissionsState().hasRequestedPermission(permissionNames)) {
+ && !permissionsState.hasPermissionState(permissionNames)) {
continue;
}
@@ -4948,8 +4945,8 @@
pw.println("Packages:");
printedSomething = true;
}
- dumpPackageLPr(pw, " ", checkin ? "pkg" : null, permissionNames, ps, sdf, date, users,
- packageName != null, dumpAllComponents);
+ dumpPackageLPr(pw, " ", checkin ? "pkg" : null, permissionNames, ps, permissionsState,
+ sdf, date, users, packageName != null, dumpAllComponents);
}
printedSomething = false;
@@ -4989,8 +4986,10 @@
pw.println("Hidden system packages:");
printedSomething = true;
}
- dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps, sdf, date,
- users, packageName != null, dumpAllComponents);
+ final AppIdPermissionState permissionsState =
+ mPermissionDataProvider.getAppIdPermissionState(ps.appId);
+ dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps,
+ permissionsState, sdf, date, users, packageName != null, dumpAllComponents);
}
}
}
@@ -5018,8 +5017,10 @@
if (packageName != null && su != dumpState.getSharedUser()) {
continue;
}
+ final AppIdPermissionState permissionsState =
+ mPermissionDataProvider.getAppIdPermissionState(su.userId);
if (permissionNames != null
- && !su.getPermissionsState().hasRequestedPermission(permissionNames)) {
+ && !permissionsState.hasPermissionState(permissionNames)) {
continue;
}
if (!checkin) {
@@ -5054,12 +5055,12 @@
continue;
}
- final PermissionsState permissionsState = su.getPermissionsState();
dumpInstallPermissionsLPr(pw, prefix, permissionNames, permissionsState);
for (int userId : UserManagerService.getInstance().getUserIds()) {
- final int[] gids = permissionsState.computeGids(userId);
- final List<PermissionState> permissions =
+ final int[] gids = mPermissionDataProvider.getGidsForUid(UserHandle.getUid(
+ userId, su.userId));
+ final Collection<PermissionState> permissions =
permissionsState.getRuntimePermissionStates(userId);
if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
@@ -5120,7 +5121,7 @@
}
void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
- List<PermissionState> permissionStates, boolean dumpAll) {
+ Collection<PermissionState> permissionStates, boolean dumpAll) {
if (!permissionStates.isEmpty() || dumpAll) {
pw.print(prefix); pw.println("runtime permissions:");
for (PermissionState permissionState : permissionStates) {
@@ -5161,8 +5162,9 @@
}
void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, ArraySet<String> permissionNames,
- PermissionsState permissionsState) {
- List<PermissionState> permissionStates = permissionsState.getInstallPermissionStates();
+ AppIdPermissionState permissionsState) {
+ Collection<PermissionState> permissionStates =
+ permissionsState.getInstallPermissionStates();
if (!permissionStates.isEmpty()) {
pw.print(prefix); pw.println("install permissions:");
for (PermissionState permissionState : permissionStates) {
@@ -5202,9 +5204,9 @@
public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
if (sync) {
- mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserSyncLPr(userId);
} else {
- mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
+ mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
}
}
@@ -5292,8 +5294,6 @@
private final Handler mHandler = new MyHandler();
- private final Object mPersistenceLock;
-
@GuardedBy("mLock")
private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
@@ -5313,10 +5313,8 @@
// The mapping keys are user ids.
private final SparseBooleanArray mPermissionUpgradeNeeded = new SparseBooleanArray();
- public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence,
- Object persistenceLock) {
+ public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence) {
mPersistence = persistence;
- mPersistenceLock = persistenceLock;
}
@GuardedBy("Settings.this.mLock")
@@ -5327,7 +5325,7 @@
@GuardedBy("Settings.this.mLock")
void setVersionLPr(int version, int userId) {
mVersions.put(userId, version);
- writePermissionsForUserAsyncLPr(userId);
+ writeStateForUserAsyncLPr(userId);
}
@GuardedBy("Settings.this.mLock")
@@ -5342,7 +5340,7 @@
+ "set before trying to update the fingerprint.");
}
mFingerprints.put(userId, mExtendedFingerprint);
- writePermissionsForUserAsyncLPr(userId);
+ writeStateForUserAsyncLPr(userId);
}
public void setPermissionControllerVersion(long version) {
@@ -5361,13 +5359,7 @@
return Build.FINGERPRINT + "?pc_version=" + version;
}
- public void writePermissionsForUserSyncLPr(int userId) {
- mHandler.removeMessages(userId);
- writePermissionsSync(userId);
- }
-
- @GuardedBy("Settings.this.mLock")
- public void writePermissionsForUserAsyncLPr(int userId) {
+ public void writeStateForUserAsyncLPr(int userId) {
final long currentTimeMillis = SystemClock.uptimeMillis();
if (mWriteScheduled.get(userId)) {
@@ -5399,59 +5391,53 @@
}
}
- private void writePermissionsSync(int userId) {
- RuntimePermissionsState runtimePermissions;
- synchronized (mPersistenceLock) {
- mWriteScheduled.delete(userId);
+ public void writeStateForUserSyncLPr(int userId) {
+ mHandler.removeMessages(userId);
+ mWriteScheduled.delete(userId);
- int version = mVersions.get(userId, INITIAL_VERSION);
+ int version = mVersions.get(userId, INITIAL_VERSION);
- String fingerprint = mFingerprints.get(userId);
+ String fingerprint = mFingerprints.get(userId);
- Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
- new ArrayMap<>();
- int packagesSize = mPackages.size();
- for (int i = 0; i < packagesSize; i++) {
- String packageName = mPackages.keyAt(i);
- PackageSetting packageSetting = mPackages.valueAt(i);
- if (packageSetting.sharedUser == null) {
- List<RuntimePermissionsState.PermissionState> permissions =
- getPermissionsFromPermissionsState(
- packageSetting.getPermissionsState(), userId);
- packagePermissions.put(packageName, permissions);
- }
- }
-
- Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
- new ArrayMap<>();
- final int sharedUsersSize = mSharedUsers.size();
- for (int i = 0; i < sharedUsersSize; i++) {
- String sharedUserName = mSharedUsers.keyAt(i);
- SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
+ Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
+ new ArrayMap<>();
+ int packagesSize = mPackages.size();
+ for (int i = 0; i < packagesSize; i++) {
+ String packageName = mPackages.keyAt(i);
+ PackageSetting packageSetting = mPackages.valueAt(i);
+ if (packageSetting.sharedUser == null) {
List<RuntimePermissionsState.PermissionState> permissions =
getPermissionsFromPermissionsState(
- sharedUserSetting.getPermissionsState(), userId);
- sharedUserPermissions.put(sharedUserName, permissions);
+ packageSetting.getPermissionsState(), userId);
+ packagePermissions.put(packageName, permissions);
}
-
- runtimePermissions = new RuntimePermissionsState(version, fingerprint,
- packagePermissions, sharedUserPermissions);
}
+ Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
+ new ArrayMap<>();
+ final int sharedUsersSize = mSharedUsers.size();
+ for (int i = 0; i < sharedUsersSize; i++) {
+ String sharedUserName = mSharedUsers.keyAt(i);
+ SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
+ List<RuntimePermissionsState.PermissionState> permissions =
+ getPermissionsFromPermissionsState(
+ sharedUserSetting.getPermissionsState(), userId);
+ sharedUserPermissions.put(sharedUserName, permissions);
+ }
+
+ RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
+ fingerprint, packagePermissions, sharedUserPermissions);
+
mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
}
@NonNull
private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState(
- @NonNull PermissionsState permissionsState, @UserIdInt int userId) {
- List<PermissionState> permissionStates = permissionsState.getRuntimePermissionStates(
- userId);
- List<RuntimePermissionsState.PermissionState> permissions =
- new ArrayList<>();
- int permissionStatesSize = permissionStates.size();
- for (int i = 0; i < permissionStatesSize; i++) {
- PermissionState permissionState = permissionStates.get(i);
-
+ @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) {
+ Collection<PermissionState> permissionStates =
+ permissionsState.getRuntimePermissionStates(userId);
+ List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>();
+ for (PermissionState permissionState : permissionStates) {
RuntimePermissionsState.PermissionState permission =
new RuntimePermissionsState.PermissionState(permissionState.getName(),
permissionState.isGranted(), permissionState.getFlags());
@@ -5480,7 +5466,7 @@
userId));
if (runtimePermissions == null) {
readLegacyStateForUserSyncLPr(userId);
- writePermissionsForUserAsyncLPr(userId);
+ writeStateForUserAsyncLPr(userId);
return;
}
@@ -5536,7 +5522,7 @@
private void readPermissionsStateLpr(
@NonNull List<RuntimePermissionsState.PermissionState> permissions,
- @NonNull PermissionsState permissionsState, @UserIdInt int userId) {
+ @NonNull AppIdPermissionState permissionsState, @UserIdInt int userId) {
int permissionsSize = permissions.size();
for (int i = 0; i < permissionsSize; i++) {
RuntimePermissionsState.PermissionState permission = permissions.get(i);
@@ -5550,14 +5536,8 @@
boolean granted = permission.isGranted();
int flags = permission.getFlags();
- if (granted) {
- permissionsState.grantRuntimePermission(basePermission, userId);
- permissionsState.updatePermissionFlags(basePermission, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- } else {
- permissionsState.updatePermissionFlags(basePermission, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- }
+ permissionsState.putRuntimePermissionState(new PermissionState(basePermission,
+ granted, flags), userId);
}
}
@@ -5638,8 +5618,9 @@
}
}
- private void parsePermissionsLPr(XmlPullParser parser, PermissionsState permissionsState,
- int userId) throws IOException, XmlPullParserException {
+ private void parsePermissionsLPr(XmlPullParser parser,
+ AppIdPermissionState permissionsState, int userId)
+ throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -5666,15 +5647,8 @@
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
- if (granted) {
- permissionsState.grantRuntimePermission(bp, userId);
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- } else {
- permissionsState.updatePermissionFlags(bp, userId,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
- }
-
+ permissionsState.putRuntimePermissionState(new PermissionState(bp, granted,
+ flags), userId);
}
break;
}
@@ -5690,7 +5664,9 @@
public void handleMessage(Message message) {
final int userId = message.what;
Runnable callback = (Runnable) message.obj;
- writePermissionsSync(userId);
+ synchronized (mLock) {
+ writeStateForUserSyncLPr(userId);
+ }
if (callback != null) {
callback.run();
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index d413213..35c26d6 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -86,7 +86,6 @@
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
-import java.util.function.Predicate;
import java.util.function.Supplier;
/**
@@ -226,7 +225,7 @@
final IntArray childSessionIds = new IntArray();
if (session.isMultiPackage()) {
for (PackageInstallerSession s : session.getChildSessions()) {
- if (isApexSession(s)) {
+ if (s.isApexSession()) {
childSessionIds.add(s.sessionId);
}
}
@@ -329,31 +328,6 @@
}
}
- private static boolean isApexSession(@NonNull PackageInstallerSession session) {
- return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0;
- }
-
- private boolean sessionContains(@NonNull PackageInstallerSession session,
- Predicate<PackageInstallerSession> filter) {
- if (!session.isMultiPackage()) {
- return filter.test(session);
- }
- for (PackageInstallerSession s : session.getChildSessions()) {
- if (filter.test(s)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean sessionContainsApex(@NonNull PackageInstallerSession session) {
- return sessionContains(session, (s) -> isApexSession(s));
- }
-
- private boolean sessionContainsApk(@NonNull PackageInstallerSession session) {
- return sessionContains(session, (s) -> !isApexSession(s));
- }
-
// Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
private void abortCheckpoint(int sessionId, String errorMsg) {
String failureReason = "Failed to install sessionId: " + sessionId + " Error: " + errorMsg;
@@ -400,7 +374,7 @@
List<PackageInstallerSession> apexSessions = new ArrayList<>();
if (session.isMultiPackage()) {
for (PackageInstallerSession s : session.getChildSessions()) {
- if (sessionContainsApex(s)) {
+ if (s.containsApexSession()) {
apexSessions.add(s);
}
}
@@ -521,7 +495,7 @@
private void resumeSession(@NonNull PackageInstallerSession session) {
Slog.d(TAG, "Resuming session " + session.sessionId);
- final boolean hasApex = sessionContainsApex(session);
+ final boolean hasApex = session.containsApexSession();
ApexSessionInfo apexSessionInfo = null;
if (hasApex) {
// Check with apexservice whether the apex packages have been activated.
@@ -740,7 +714,7 @@
@Nullable
private PackageInstallerSession extractApksInSession(PackageInstallerSession session)
throws PackageManagerException {
- if (!session.isMultiPackage() && !isApexSession(session)) {
+ if (!session.isMultiPackage() && !session.isApexSession()) {
return createAndWriteApkSession(session);
} else if (session.isMultiPackage()) {
// For multi-package staged sessions containing APKs, we identify which child sessions
@@ -749,7 +723,7 @@
// sessions will be installed atomically.
final List<PackageInstallerSession> childSessions = new ArrayList<>();
for (PackageInstallerSession s : session.getChildSessions()) {
- if (!isApexSession(s)) {
+ if (!s.isApexSession()) {
childSessions.add(s);
}
}
@@ -798,7 +772,7 @@
}
final Set<String> apkNames = new ArraySet<>();
for (PackageInstallerSession s : session.getChildSessions()) {
- if (!isApexSession(s)) {
+ if (!s.isApexSession()) {
apkNames.add(s.getPackageName());
}
}
@@ -1020,7 +994,7 @@
* @return returns true if it is ensured that there is no active apex session, otherwise false
*/
private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) {
- if (!sessionContainsApex(session)) {
+ if (!session.containsApexSession()) {
return true;
}
final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
@@ -1342,7 +1316,7 @@
*/
private void handlePreRebootVerification_Apex(
@NonNull PackageInstallerSession session, int rollbackId) {
- final boolean hasApex = sessionContainsApex(session);
+ final boolean hasApex = session.containsApexSession();
// APEX checks. For single-package sessions, check if they contain an APEX. For
// multi-package sessions, find all the child sessions that contain an APEX.
@@ -1372,7 +1346,7 @@
* {@link #notifyPreRebootVerification_Apk_Complete}
*/
private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
- if (!sessionContainsApk(session)) {
+ if (!session.containsApkSession()) {
notifyPreRebootVerification_Apk_Complete(session);
return;
}
@@ -1421,7 +1395,7 @@
Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
session.setStagedSessionReady();
if (session.isStagedSessionReady()) {
- final boolean hasApex = sessionContainsApex(session);
+ final boolean hasApex = session.containsApexSession();
if (hasApex) {
try {
mApexManager.markStagedSessionReady(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4c58b06..6b29129 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4709,7 +4709,7 @@
final boolean running = am.isUserRunning(user.id, 0);
final boolean current = user.id == currentUser;
if (verbose) {
- pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s\n", i, user.id, user.name,
+ pw.printf("%d: id=%d, name=%s, flags=%s%s%s%s%s%s\n", i, user.id, user.name,
UserInfo.flagsToString(user.flags),
running ? " (running)" : "",
user.partial ? " (partial)" : "",
diff --git a/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java b/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java
new file mode 100644
index 0000000..aabdafd
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/AppIdPermissionState.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Objects;
+
+/**
+ * Legacy permission state that was associated with packages or shared users.
+ */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public final class AppIdPermissionState {
+ // Maps from user IDs to user states.
+ @NonNull
+ private final SparseArray<UserState> mUserStates = new SparseArray<>();
+
+ // Keyed by user IDs.
+ @NonNull
+ private final SparseBooleanArray mMissing = new SparseBooleanArray();
+
+ /**
+ * Copy from another permission state.
+ *
+ * @param other the other permission state.
+ *
+ * @hide
+ */
+ public void copyFrom(@NonNull AppIdPermissionState other) {
+ if (other == this) {
+ return;
+ }
+
+ mUserStates.clear();
+ final int userStatesSize = other.mUserStates.size();
+ for (int i = 0; i < userStatesSize; i++) {
+ mUserStates.put(other.mUserStates.keyAt(i),
+ new UserState(other.mUserStates.valueAt(i)));
+ }
+
+ mMissing.clear();
+ final int missingSize = other.mMissing.size();
+ for (int i = 0; i < missingSize; i++) {
+ mMissing.put(other.mMissing.keyAt(i), other.mMissing.valueAt(i));
+ }
+ }
+
+ /**
+ * Reset this permission state.
+ *
+ * @hide
+ */
+ public void reset() {
+ mUserStates.clear();
+ mMissing.clear();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null) {
+ return false;
+ }
+ if (getClass() != object.getClass()) {
+ return false;
+ }
+ final AppIdPermissionState other = (AppIdPermissionState) object;
+ return Objects.equals(mUserStates, other.mUserStates)
+ && Objects.equals(mMissing, other.mMissing);
+ }
+
+ /**
+ * Put a install permission state.
+ *
+ * @param permissionState the permission state
+ */
+ public void putInstallPermissionState(@NonNull PermissionState permissionState) {
+ putPermissionState(permissionState, UserHandle.USER_ALL);
+ }
+
+ /**
+ * Put a runtime permission state for a user.
+ *
+ * @param permissionState the permission state
+ * @param userId the user ID
+ */
+ public void putRuntimePermissionState(@NonNull PermissionState permissionState,
+ @UserIdInt int userId) {
+ checkUserId(userId);
+ putPermissionState(permissionState, userId);
+ }
+
+ private void putPermissionState(@NonNull PermissionState permissionState,
+ @UserIdInt int userId) {
+ UserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ userState = new UserState();
+ mUserStates.put(userId, userState);
+ }
+ userState.putPermissionState(permissionState);
+ }
+
+ /**
+ * Check whether there are any permission states for the given permissions.
+ *
+ * @param permissionNames the permission names
+ * @return whether there are any permission states
+ *
+ * @hide
+ */
+ public boolean hasPermissionState(@NonNull Collection<String> permissionNames) {
+ final int userStatesSize = mUserStates.size();
+ for (int i = 0; i < userStatesSize; i++) {
+ final UserState userState = mUserStates.valueAt(i);
+ for (final String permissionName : permissionNames) {
+ if (userState.getPermissionState(permissionName) != null) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get all the install permission states.
+ *
+ * @return the install permission states
+ */
+ @NonNull
+ public Collection<PermissionState> getInstallPermissionStates() {
+ return getPermissionStates(UserHandle.USER_ALL);
+ }
+
+ /**
+ * Get all the runtime permission states for a user.
+ *
+ * @param userId the user ID
+ * @return the runtime permission states
+ */
+ @NonNull
+ public Collection<PermissionState> getRuntimePermissionStates(@UserIdInt int userId) {
+ checkUserId(userId);
+ return getPermissionStates(userId);
+ }
+
+ @NonNull
+ private Collection<PermissionState> getPermissionStates(@UserIdInt int userId) {
+ final UserState userState = mUserStates.get(userId);
+ if (userState == null) {
+ return Collections.emptyList();
+ }
+ return userState.getPermissionStates();
+ }
+
+ /**
+ * Check whether the permission state is missing for a user.
+ * <p>
+ * This can happen if permission state is rolled back and we'll need to generate a reasonable
+ * default state to keep the app usable.
+ *
+ * @param userId the user ID
+ * @return whether the permission state is missing
+ */
+ public boolean isMissing(@UserIdInt int userId) {
+ checkUserId(userId);
+ return mMissing.get(userId);
+ }
+
+ /**
+ * Set whether the permission state is missing for a user.
+ * <p>
+ * This can happen if permission state is rolled back and we'll need to generate a reasonable
+ * default state to keep the app usable.
+ *
+ * @param missing whether the permission state is missing
+ * @param userId the user ID
+ */
+ public void setMissing(boolean missing, @UserIdInt int userId) {
+ checkUserId(userId);
+ if (missing) {
+ mMissing.put(userId, true);
+ } else {
+ mMissing.delete(userId);
+ }
+ }
+
+ private static void checkUserId(@UserIdInt int userId) {
+ if (userId < 0) {
+ throw new IllegalArgumentException("Invalid user ID " + userId);
+ }
+ }
+
+ /**
+ * Legacy state for permissions for a user.
+ */
+ private static final class UserState {
+ // Maps from permission names to permission states.
+ @NonNull
+ private final ArrayMap<String, PermissionState> mPermissionStates = new ArrayMap<>();
+
+ public UserState() {}
+
+ public UserState(@NonNull UserState other) {
+ final int permissionStatesSize = other.mPermissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ mPermissionStates.put(other.mPermissionStates.keyAt(i),
+ new PermissionState(other.mPermissionStates.valueAt(i)));
+ }
+ }
+
+ @Nullable
+ public PermissionState getPermissionState(@NonNull String permissionName) {
+ return mPermissionStates.get(permissionName);
+ }
+
+ public void putPermissionState(@NonNull PermissionState permissionState) {
+ mPermissionStates.put(permissionState.getName(), permissionState);
+ }
+
+ @NonNull
+ public Collection<PermissionState> getPermissionStates() {
+ return Collections.unmodifiableCollection(mPermissionStates.values());
+ }
+ }
+
+ /**
+ * Legacy state for a single permission.
+ */
+ public static final class PermissionState {
+ @NonNull
+ private final BasePermission mPermission;
+
+ private final boolean mGranted;
+
+ private final int mFlags;
+
+ /**
+ * Create a new instance of this class.
+ *
+ * @param permission the {@link BasePermission} for the permission
+ * @param granted whether the permission is granted
+ * @param flags the permission flags
+ */
+ public PermissionState(@NonNull BasePermission permission, boolean granted, int flags) {
+ mPermission = permission;
+ mGranted = granted;
+ mFlags = flags;
+ }
+
+ private PermissionState(@NonNull PermissionState other) {
+ mPermission = other.mPermission;
+ mGranted = other.mGranted;
+ mFlags = other.mFlags;
+ }
+
+ /**
+ * Get the {@link BasePermission} for the permission.
+ *
+ * @return the {@link BasePermission}
+ */
+ @NonNull
+ public BasePermission getPermission() {
+ return mPermission;
+ }
+
+ /**
+ * Get the permission name.
+ *
+ * @return the permission name
+ */
+ @NonNull
+ public String getName() {
+ return mPermission.getName();
+ }
+
+ /**
+ * Get whether the permission is granted.
+ *
+ * @return whether the permission is granted
+ */
+ @NonNull
+ public boolean isGranted() {
+ return mGranted;
+ }
+
+ /**
+ * Get the permission flags.
+ *
+ * @return the permission flags
+ */
+ @NonNull
+ public int getFlags() {
+ return mFlags;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
new file mode 100644
index 0000000..7452b52
--- /dev/null
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionDataProvider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.permission;
+
+import android.annotation.AppIdInt;
+import android.annotation.NonNull;
+
+/**
+ * An interface for legacy code to read permission data in order to maintain compatibility.
+ */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+public interface LegacyPermissionDataProvider {
+ /**
+ * Get the legacy permission state of an app ID, either a package or a shared user.
+ *
+ * @param appId the app ID
+ * @return the legacy permission state
+ */
+ @NonNull
+ public abstract AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId);
+
+ /**
+ * Get the GIDs computed from the permission state of a UID, either a package or a shared user.
+ *
+ * @param uid the UID
+ * @return the GIDs for the UID
+ */
+ @NonNull
+ public abstract int[] getGidsForUid(int uid);
+}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index aa327ba..840b233 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -58,6 +58,7 @@
import static java.util.concurrent.TimeUnit.SECONDS;
import android.Manifest;
+import android.annotation.AppIdInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -137,7 +138,6 @@
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
-import com.android.server.am.ActivityManagerService;
import com.android.server.pm.ApexManager;
import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageSetting;
@@ -183,8 +183,6 @@
private static final int GRANT_INSTALL = 2;
/** Permission grant: grant the permission as a runtime one. */
private static final int GRANT_RUNTIME = 3;
- /** Permission grant: grant as runtime a permission that was granted as an install time one. */
- private static final int GRANT_UPGRADE = 4;
private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
@@ -902,16 +900,6 @@
}
private int checkPermissionImpl(String permName, String pkgName, int userId) {
- try {
- enforceCrossUserOrProfilePermission(Binder.getCallingUid(), userId,
- false, false, "checkPermissionImpl");
- } catch (Exception e) {
- Slog.e(TAG, "Invalid cross user access", e);
- EventLog.writeEvent(0x534e4554, "153996875", "checkPermissionImpl", pkgName);
-
- throw e;
- }
-
final AndroidPackage pkg = mPackageManagerInt.getPackage(pkgName);
if (pkg == null) {
return PackageManager.PERMISSION_DENIED;
@@ -989,16 +977,6 @@
}
private int checkUidPermissionImpl(String permName, int uid) {
- try {
- enforceCrossUserOrProfilePermission(Binder.getCallingUid(), UserHandle.getUserId(uid),
- false, false, "checkUidPermissionImpl");
- } catch (Exception e) {
- Slog.e(TAG, "Invalid cross user access", e);
- EventLog.writeEvent(0x534e4554, "153996875", "checkUidPermissionImpl", uid);
-
- throw e;
- }
-
final AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
return checkUidPermissionInternal(pkg, uid, permName);
}
@@ -2535,20 +2513,6 @@
return permission.computeGids(userId);
}
- @Nullable
- private int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) {
- final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName);
- if (ps == null) {
- return null;
- }
- final UidPermissionState uidState = getUidState(ps, userId);
- if (uidState == null) {
- Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId);
- return null;
- }
- return uidState.computeGids(userId);
- }
-
/**
* Restore the permission state for a package.
*
@@ -2721,7 +2685,7 @@
SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
String splitPermName = sp.getSplitPermission();
if (sp.getNewPermissions().contains(permName)
- && origState.hasInstallPermission(splitPermName)) {
+ && origState.hasPermission(splitPermName)) {
upgradedActivityRecognitionPermission = splitPermName;
newImplicitPermissions.add(permName);
@@ -2767,15 +2731,8 @@
// For all apps normal permissions are install time ones.
grant = GRANT_INSTALL;
} else if (bp.isRuntime()) {
- if (origState.hasInstallPermission(bp.getName())
- || upgradedActivityRecognitionPermission != null) {
- // Before Q we represented some runtime permissions as install permissions,
- // in Q we cannot do this anymore. Hence upgrade them all.
- grant = GRANT_UPGRADE;
- } else {
- // For modern apps keep runtime permissions unchanged.
- grant = GRANT_RUNTIME;
- }
+ // For modern apps keep runtime permissions unchanged.
+ grant = GRANT_RUNTIME;
} else if (bp.isSignature()) {
// For all apps signature permissions are install time ones.
allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origState);
@@ -2784,19 +2741,16 @@
}
}
- if (grant != GRANT_DENIED) {
- if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name)
- && !bp.isRuntime()) {
- // If this is an existing, non-system package, then
- // we can't add any new permissions to it. Runtime
- // permissions can be added any time - they ad dynamic.
- if (!allowedSig && !origState.hasInstallPermission(perm)) {
- // Except... if this is a permission that was added
- // to the platform (note: need to only do this when
- // updating the platform).
- if (!isNewPlatformPermissionForPackage(perm, pkg)) {
- grant = GRANT_DENIED;
- }
+ if (grant == GRANT_INSTALL && !allowedSig && !origState.hasPermission(perm)) {
+ // If this is an existing, non-system package, then
+ // we can't add any new permissions to it. Runtime
+ // permissions can be added any time - they are dynamic.
+ if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name)) {
+ // Except... if this is a permission that was added
+ // to the platform (note: need to only do this when
+ // updating the platform).
+ if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+ grant = GRANT_DENIED;
}
}
}
@@ -2810,22 +2764,6 @@
if (grant != GRANT_DENIED) {
switch (grant) {
case GRANT_INSTALL: {
- // Revoke this as runtime permission to handle the case of
- // a runtime permission being downgraded to an install one.
- // Also in permission review mode we keep dangerous permissions
- // for legacy apps
- final PermissionState origPermissionState =
- origState.getPermissionState(perm);
- if (origPermissionState != null
- && origPermissionState.isRuntime()) {
- // Revoke the runtime permission and clear the flags.
- origState.revokePermission(bp);
- origState.updatePermissionFlags(bp,
- PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
- // If we revoked a permission permission, we have to write.
- updatedUserIds = ArrayUtils.appendInt(
- updatedUserIds, userId);
- }
// Grant an install permission.
if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) {
changedInstallPermission = true;
@@ -2890,7 +2828,8 @@
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
|| (!hardRestricted || restrictionExempt)) {
- if (origPermState != null && origPermState.isGranted()) {
+ if ((origPermState != null && origPermState.isGranted())
+ || upgradedActivityRecognitionPermission != null) {
if (uidState.grantPermission(bp)
== PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
@@ -2948,124 +2887,6 @@
flags);
} break;
- case GRANT_UPGRADE: {
- // Upgrade from Pre-Q to Q permission model. Make all permissions
- // runtime
- PermissionState origPermState = origState.getPermissionState(perm);
- int flags = (origPermState != null) ? origPermState.getFlags() : 0;
-
- BasePermission bpToRevoke =
- upgradedActivityRecognitionPermission == null
- ? bp : mSettings.getPermissionLocked(
- upgradedActivityRecognitionPermission);
- // Remove install permission
- if (origState.revokePermission(bpToRevoke)
- != PERMISSION_OPERATION_FAILURE) {
- origState.updatePermissionFlags(bpToRevoke,
- (MASK_PERMISSION_FLAGS_ALL
- & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
- changedInstallPermission = true;
- }
-
- boolean hardRestricted = bp.isHardRestricted();
- boolean softRestricted = bp.isSoftRestricted();
-
- // If permission policy is not ready we don't deal with restricted
- // permissions as the policy may whitelist some permissions. Once
- // the policy is initialized we would re-evaluate permissions.
- final boolean permissionPolicyInitialized =
- mPermissionPolicyInternal != null
- && mPermissionPolicyInternal.isInitialized(userId);
-
- boolean wasChanged = false;
-
- boolean restrictionExempt =
- (origState.getPermissionFlags(bp.name)
- & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
- boolean restrictionApplied = (origState.getPermissionFlags(
- bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
-
- if (appSupportsRuntimePermissions) {
- // If hard restricted we don't allow holding it
- if (permissionPolicyInitialized && hardRestricted) {
- if (!restrictionExempt) {
- if (origPermState != null && origPermState.isGranted()
- && uidState.revokePermission(
- bp) != PERMISSION_OPERATION_FAILURE) {
- wasChanged = true;
- }
- if (!restrictionApplied) {
- flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
- wasChanged = true;
- }
- }
- // If soft restricted we allow holding in a restricted form
- } else if (permissionPolicyInitialized && softRestricted) {
- // Regardless if granted set the restriction flag as it
- // may affect app treatment based on this permission.
- if (!restrictionExempt && !restrictionApplied) {
- flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
- wasChanged = true;
- }
- }
-
- // Remove review flag as it is not necessary anymore
- if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
- wasChanged = true;
- }
-
- if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
- flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
- wasChanged = true;
- // Hard restricted permissions cannot be held.
- } else if (!permissionPolicyInitialized ||
- (!hardRestricted || restrictionExempt)) {
- if (uidState.grantPermission(bp)
- != PERMISSION_OPERATION_FAILURE) {
- wasChanged = true;
- }
- }
- } else {
- if (!uidState.hasPermission(bp.name)
- && uidState.grantPermission(bp)
- != PERMISSION_OPERATION_FAILURE) {
- flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
- wasChanged = true;
- }
-
- // If legacy app always grant the permission but if restricted
- // and not exempt take a note a restriction should be applied.
- if (permissionPolicyInitialized
- && (hardRestricted || softRestricted)
- && !restrictionExempt && !restrictionApplied) {
- flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
- wasChanged = true;
- }
- }
-
- // If unrestricted or restriction exempt, don't apply restriction.
- if (permissionPolicyInitialized) {
- if (!(hardRestricted || softRestricted) || restrictionExempt) {
- if (restrictionApplied) {
- flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
- // Dropping restriction on a legacy app implies a review
- if (!appSupportsRuntimePermissions) {
- flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
- }
- wasChanged = true;
- }
- }
- }
-
- if (wasChanged) {
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- }
-
- uidState.updatePermissionFlags(bp,
- MASK_PERMISSION_FLAGS_ALL, flags);
- } break;
-
default: {
if (packageOfInterest == null
|| packageOfInterest.equals(pkg.getPackageName())) {
@@ -3174,11 +2995,11 @@
for (String permission : ps.getPermissions()) {
if (!pkg.getImplicitPermissions().contains(permission)) {
- if (!ps.hasInstallPermission(permission)) {
+ BasePermission bp = mSettings.getPermissionLocked(permission);
+ if (bp.isRuntime()) {
int flags = ps.getPermissionFlags(permission);
if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
- BasePermission bp = mSettings.getPermissionLocked(permission);
int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
@@ -3324,8 +3145,8 @@
ArraySet<String> sourcePerms = newToSplitPerms.get(newPerm);
if (sourcePerms != null) {
- if (!ps.hasInstallPermission(newPerm)) {
- BasePermission bp = mSettings.getPermissionLocked(newPerm);
+ BasePermission bp = mSettings.getPermissionLocked(newPerm);
+ if (bp.isRuntime()) {
if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) {
ps.updatePermissionFlags(bp,
@@ -3334,27 +3155,31 @@
}
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- boolean inheritsFromInstallPerm = false;
- for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
- sourcePermNum++) {
- if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) {
- inheritsFromInstallPerm = true;
- break;
+ if (!origPs.hasRequestedPermission(sourcePerms)) {
+ boolean inheritsFromInstallPerm = false;
+ for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
+ sourcePermNum++) {
+ final String sourcePerm = sourcePerms.valueAt(sourcePermNum);
+ BasePermission sourceBp = mSettings.getPermissionLocked(sourcePerm);
+ if (!sourceBp.isRuntime()) {
+ inheritsFromInstallPerm = true;
+ break;
+ }
+ }
+
+ if (!inheritsFromInstallPerm) {
+ // Both permissions are new so nothing to inherit.
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
+ + " for " + pkgName + " as split permission is also new");
+ }
+ continue;
}
}
- if (!origPs.hasRequestedPermission(sourcePerms)
- && !inheritsFromInstallPerm) {
- // Both permissions are new so nothing to inherit.
- if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
- + " for " + pkgName + " as split permission is also new");
- }
- } else {
- // Inherit from new install or existing runtime permissions
- inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
- newPerm, ps, pkg);
- }
+ // Inherit from new install or existing runtime permissions
+ inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, newPerm, ps,
+ pkg);
}
}
}
@@ -3640,7 +3465,7 @@
if (!allowed && bp.isDevelopment()) {
// For development permissions, a development permission
// is granted only if it was already granted.
- allowed = origPermissions.hasInstallPermission(perm);
+ allowed = origPermissions.hasPermission(perm);
}
if (!allowed && bp.isSetup()
&& ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
@@ -4529,7 +4354,7 @@
}
final int callingUserId = UserHandle.getUserId(callingUid);
if (hasCrossUserPermission(
- Binder.getCallingPid(), callingUid, callingUserId, userId, requireFullPermission,
+ callingUid, callingUserId, userId, requireFullPermission,
requirePermissionWhenSameUser)) {
return;
}
@@ -4556,79 +4381,53 @@
private void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
String message) {
- int callingPid = Binder.getCallingPid();
- final int callingUserId = UserHandle.getUserId(callingUid);
-
if (userId < 0) {
throw new IllegalArgumentException("Invalid userId " + userId);
}
-
- if (callingUserId == userId) {
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ final int callingUserId = UserHandle.getUserId(callingUid);
+ if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
+ /*requirePermissionWhenSameUser= */ false)) {
return;
}
-
- // Prevent endless loop between when checking permission while checking a permission
- if (callingPid == ActivityManagerService.MY_PID) {
+ final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
+ if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
+ mContext,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+ PermissionChecker.PID_UNKNOWN,
+ callingUid,
+ mPackageManagerInt.getPackage(callingUid).getPackageName())
+ == PermissionChecker.PERMISSION_GRANTED) {
return;
}
-
- long token = Binder.clearCallingIdentity();
- try {
- if (checkShell) {
- PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
- UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
- }
- if (hasCrossUserPermission(callingPid, callingUid, callingUserId, userId,
- requireFullPermission, /*requirePermissionWhenSameUser= */ false)) {
- return;
- }
- final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
-
- if (isSameProfileGroup) {
- AndroidPackage callingPkg = mPackageManagerInt.getPackage(callingUid);
- String callingPkgName = null;
- if (callingPkg != null) {
- callingPkgName = callingPkg.getPackageName();
- }
-
- if (PermissionChecker.checkPermissionForPreflight(
- mContext,
- android.Manifest.permission.INTERACT_ACROSS_PROFILES,
- PermissionChecker.PID_UNKNOWN,
- callingUid,
- callingPkgName)
- == PermissionChecker.PERMISSION_GRANTED) {
- return;
- }
- }
-
String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
callingUid, userId, message, requireFullPermission, isSameProfileGroup);
Slog.w(TAG, errorMessage);
throw new SecurityException(errorMessage);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
- private boolean hasCrossUserPermission(int callingPid, int callingUid, int callingUserId,
- int userId, boolean requireFullPermission, boolean requirePermissionWhenSameUser) {
+ private boolean hasCrossUserPermission(
+ int callingUid, int callingUserId, int userId, boolean requireFullPermission,
+ boolean requirePermissionWhenSameUser) {
if (!requirePermissionWhenSameUser && userId == callingUserId) {
return true;
}
if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) {
return true;
}
-
- if (!requireFullPermission) {
- if (mContext.checkPermission(android.Manifest.permission.INTERACT_ACROSS_USERS,
- callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) {
- return true;
- }
+ if (requireFullPermission) {
+ return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
}
+ return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
+ }
- return mContext.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission)
+ == PackageManager.PERMISSION_GRANTED;
}
private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
@@ -4813,7 +4612,7 @@
}
}
- private void removeAppState(int appId) {
+ private void removeAppIdState(@AppIdInt int appId) {
synchronized (mLock) {
final int[] userIds = mState.getUserIds();
for (final int userId : userIds) {
@@ -4827,7 +4626,7 @@
final int[] userIds = getAllUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
final int appId = ps.getAppId();
- final PermissionsState permissionsState = ps.getPermissionsState();
+ final AppIdPermissionState appIdState = ps.getPermissionsState();
synchronized (mLock) {
for (final int userId : userIds) {
@@ -4836,25 +4635,21 @@
userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed());
final UidPermissionState uidState = userState.getOrCreateUidState(appId);
uidState.reset();
- uidState.setGlobalGids(permissionsState.getGlobalGids());
- uidState.setMissing(permissionsState.isMissing(userId));
+ uidState.setMissing(appIdState.isMissing(userId));
readStateFromPermissionStates(uidState,
- permissionsState.getInstallPermissionStates(), false);
+ appIdState.getInstallPermissionStates());
readStateFromPermissionStates(uidState,
- permissionsState.getRuntimePermissionStates(userId), true);
+ appIdState.getRuntimePermissionStates(userId));
}
}
});
}
private void readStateFromPermissionStates(@NonNull UidPermissionState uidState,
- @NonNull List<PermissionsState.PermissionState> permissionStates, boolean isRuntime) {
- final int permissionStatesSize = permissionStates.size();
- for (int i = 0; i < permissionStatesSize; i++) {
- final PermissionsState.PermissionState permissionState = permissionStates.get(i);
- final BasePermission permission = permissionState.getPermission();
- uidState.putPermissionState(permission, isRuntime, permissionState.isGranted(),
- permissionState.getFlags());
+ @NonNull Collection<AppIdPermissionState.PermissionState> permissionStates) {
+ for (final AppIdPermissionState.PermissionState permissionState : permissionStates) {
+ uidState.putPermissionState(permissionState.getPermission(),
+ permissionState.isGranted(), permissionState.getFlags());
}
}
@@ -4862,8 +4657,8 @@
final int[] userIds = mState.getUserIds();
mPackageManagerInt.forEachPackageSetting(ps -> {
ps.setInstallPermissionsFixed(false);
- final PermissionsState permissionsState = ps.getPermissionsState();
- permissionsState.reset();
+ final AppIdPermissionState appIdState = ps.getPermissionsState();
+ appIdState.reset();
final int appId = ps.getAppId();
synchronized (mLock) {
@@ -4885,27 +4680,21 @@
continue;
}
- permissionsState.setGlobalGids(uidState.getGlobalGids());
- permissionsState.setMissing(uidState.isMissing(), userId);
+ appIdState.setMissing(uidState.isMissing(), userId);
final List<PermissionState> permissionStates = uidState.getPermissionStates();
final int permissionStatesSize = permissionStates.size();
for (int i = 0; i < permissionStatesSize; i++) {
final PermissionState permissionState = permissionStates.get(i);
- final BasePermission permission = permissionState.getPermission();
- if (permissionState.isGranted()) {
- if (permissionState.isRuntime()) {
- permissionsState.grantRuntimePermission(permission, userId);
- } else {
- permissionsState.grantInstallPermission(permission);
- }
- }
- final int flags = permissionState.getFlags();
- if (flags != 0) {
- final int flagsUserId = permissionState.isRuntime() ? userId
- : UserHandle.USER_ALL;
- permissionsState.updatePermissionFlags(permission, flagsUserId, flags,
- flags);
+ final AppIdPermissionState.PermissionState legacyPermissionState =
+ new AppIdPermissionState.PermissionState(
+ permissionState.getPermission(),
+ permissionState.isGranted(), permissionState.getFlags());
+ if (permissionState.isRuntime()) {
+ appIdState.putRuntimePermissionState(legacyPermissionState,
+ userId);
+ } else {
+ appIdState.putInstallPermissionState(legacyPermissionState);
}
}
}
@@ -4913,6 +4702,48 @@
});
}
+ @NonNull
+ private AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) {
+ final AppIdPermissionState appIdState = new AppIdPermissionState();
+ final int[] userIds = mState.getUserIds();
+ for (final int userId : userIds) {
+ final UidPermissionState uidState = getUidState(appId, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID "
+ + userId);
+ continue;
+ }
+
+ final List<PermissionState> permissionStates = uidState.getPermissionStates();
+ final int permissionStatesSize = permissionStates.size();
+ for (int i = 0; i < permissionStatesSize; i++) {
+ final PermissionState permissionState = permissionStates.get(i);
+
+ final AppIdPermissionState.PermissionState legacyPermissionState =
+ new AppIdPermissionState.PermissionState(permissionState.getPermission(),
+ permissionState.isGranted(), permissionState.getFlags());
+ if (permissionState.isRuntime()) {
+ appIdState.putRuntimePermissionState(legacyPermissionState, userId);
+ } else if (userId == UserHandle.USER_SYSTEM) {
+ appIdState.putInstallPermissionState(legacyPermissionState);
+ }
+ }
+ }
+ return appIdState;
+ }
+
+ @NonNull
+ private int[] getGidsForUid(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ final int userId = UserHandle.getUserId(uid);
+ final UidPermissionState uidState = getUidState(appId, userId);
+ if (uidState == null) {
+ Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID " + userId);
+ return EMPTY_INT_ARRAY;
+ }
+ return uidState.computeGids(userId);
+ }
+
private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
@Override
public void systemReady() {
@@ -4957,8 +4788,8 @@
PermissionManagerService.this.onUserRemoved(userId);
}
@Override
- public void removePermissionsStateTEMP(int appId) {
- PermissionManagerService.this.removeAppState(appId);
+ public void removeAppIdStateTEMP(@AppIdInt int appId) {
+ PermissionManagerService.this.removeAppIdState(appId);
}
@Override
@UserIdInt
@@ -4978,11 +4809,6 @@
public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) {
return PermissionManagerService.this.getPermissionGids(permissionName, userId);
}
- @Nullable
- @Override
- public int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId) {
- return PermissionManagerService.this.getPackageGids(packageName, userId);
- }
@Override
public void grantRequestedRuntimePermissions(AndroidPackage pkg, int[] userIds,
String[] grantedPermissions, int callingUid) {
@@ -5310,6 +5136,16 @@
}
}
}
+
+ @NonNull
+ public AppIdPermissionState getAppIdPermissionState(@AppIdInt int appId) {
+ return PermissionManagerService.this.getAppIdPermissionState(appId);
+ }
+
+ @NonNull
+ public int[] getGidsForUid(int uid) {
+ return PermissionManagerService.this.getGidsForUid(uid);
+ }
}
private static final class OnPermissionChangeListeners extends Handler {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 6d9bd2a..5ea3458 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -37,8 +37,8 @@
*
* TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes.
*/
-public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal {
-
+public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal
+ implements LegacyPermissionDataProvider {
/**
* Provider for package names.
*/
@@ -288,13 +288,13 @@
public abstract void onUserRemoved(@UserIdInt int userId);
/**
- * Remove the {@code PermissionsState} associated with an app ID, called the same time as the
+ * Remove the permission state associated with an app ID, called the same time as the
* removal of a {@code PackageSetitng}.
*
* TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app
* ID removal via API.
*/
- public abstract void removePermissionsStateTEMP(int appId);
+ public abstract void removeAppIdStateTEMP(@AppIdInt int appId);
/**
* Update the shared user setting when a package with a shared user id is removed. The gids
@@ -324,12 +324,6 @@
@Nullable
public abstract int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId);
- /**
- * Get the GIDs computed from the permission state of a package.
- */
- @Nullable
- public abstract int[] getPackageGids(@NonNull String packageName, @UserIdInt int userId);
-
/** Retrieve the packages that have requested the given app op permission */
public abstract @Nullable String[] getAppOpPermissionPackages(
@NonNull String permName, int callingUid);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java
index 2ed9a50..38264c8 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionState.java
@@ -41,13 +41,12 @@
@GuardedBy("mLock")
private int mFlags;
- public PermissionState(@NonNull BasePermission permission, boolean isRuntime) {
+ public PermissionState(@NonNull BasePermission permission) {
mPermission = permission;
- mRuntime = isRuntime;
}
public PermissionState(@NonNull PermissionState other) {
- this(other.mPermission, other.mRuntime);
+ this(other.mPermission);
mGranted = other.mGranted;
mFlags = other.mFlags;
@@ -70,7 +69,7 @@
public boolean isRuntime() {
synchronized (mLock) {
- return mRuntime;
+ return mPermission.isRuntime();
}
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java
deleted file mode 100644
index 4fb2d5f..0000000
--- a/services/core/java/com/android/server/pm/permission/PermissionsState.java
+++ /dev/null
@@ -1,970 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.permission;
-
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.ArrayUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * This class encapsulates the permissions for a package or a shared user.
- * <p>
- * There are two types of permissions: install (granted at installation)
- * and runtime (granted at runtime). Install permissions are granted to
- * all device users while runtime permissions are granted explicitly to
- * specific users.
- * </p>
- * <p>
- * The permissions are kept on a per device user basis. For example, an
- * application may have some runtime permissions granted under the device
- * owner but not granted under the secondary user.
- * <p>
- * This class is also responsible for keeping track of the Linux gids per
- * user for a package or a shared user. The gids are computed as a set of
- * the gids for all granted permissions' gids on a per user basis.
- * </p>
- */
-public final class PermissionsState {
-
- /** The permission operation failed. */
- public static final int PERMISSION_OPERATION_FAILURE = -1;
-
- /** The permission operation succeeded and no gids changed. */
- public static final int PERMISSION_OPERATION_SUCCESS = 0;
-
- /** The permission operation succeeded and gids changed. */
- public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1;
-
- private static final int[] NO_GIDS = {};
-
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private ArrayMap<String, PermissionData> mPermissions;
-
- private int[] mGlobalGids = NO_GIDS;
-
- @Nullable
- private SparseBooleanArray mMissing;
-
- private SparseBooleanArray mPermissionReviewRequired;
-
- public PermissionsState() {
- /* do nothing */
- }
-
- public PermissionsState(PermissionsState prototype) {
- copyFrom(prototype);
- }
-
- public int[] getGlobalGids() {
- return mGlobalGids;
- }
-
- /**
- * Sets the global gids, applicable to all users.
- *
- * @param globalGids The global gids.
- */
- public void setGlobalGids(int[] globalGids) {
- if (!ArrayUtils.isEmpty(globalGids)) {
- mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
- }
- }
-
- private static void invalidateCache() {
- PackageManager.invalidatePackageInfoCache();
- }
-
- /**
- * Initialized this instance from another one.
- *
- * @param other The other instance.
- */
- public void copyFrom(PermissionsState other) {
- if (other == this) {
- return;
- }
-
- synchronized (mLock) {
- if (mPermissions != null) {
- if (other.mPermissions == null) {
- mPermissions = null;
- } else {
- mPermissions.clear();
- }
- }
- if (other.mPermissions != null) {
- if (mPermissions == null) {
- mPermissions = new ArrayMap<>();
- }
- final int permissionCount = other.mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String name = other.mPermissions.keyAt(i);
- PermissionData permissionData = other.mPermissions.valueAt(i);
- mPermissions.put(name, new PermissionData(permissionData));
- }
- }
- }
-
- mGlobalGids = NO_GIDS;
- if (other.mGlobalGids != NO_GIDS) {
- mGlobalGids = Arrays.copyOf(other.mGlobalGids,
- other.mGlobalGids.length);
- }
-
- if (mMissing != null) {
- if (other.mMissing == null) {
- mMissing = null;
- } else {
- mMissing.clear();
- }
- }
- if (other.mMissing != null) {
- if (mMissing == null) {
- mMissing = new SparseBooleanArray();
- }
- final int missingSize = other.mMissing.size();
- for (int i = 0; i < missingSize; i++) {
- mMissing.put(other.mMissing.keyAt(i), other.mMissing.valueAt(i));
- }
- }
-
- if (mPermissionReviewRequired != null) {
- if (other.mPermissionReviewRequired == null) {
- mPermissionReviewRequired = null;
- } else {
- mPermissionReviewRequired.clear();
- }
- }
- if (other.mPermissionReviewRequired != null) {
- if (mPermissionReviewRequired == null) {
- mPermissionReviewRequired = new SparseBooleanArray();
- }
- final int userCount = other.mPermissionReviewRequired.size();
- for (int i = 0; i < userCount; i++) {
- final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i);
- mPermissionReviewRequired.put(other.mPermissionReviewRequired.keyAt(i),
- reviewRequired);
- }
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final PermissionsState other = (PermissionsState) obj;
-
- synchronized (mLock) {
- if (mPermissions == null) {
- if (other.mPermissions != null) {
- return false;
- }
- } else if (!mPermissions.equals(other.mPermissions)) {
- return false;
- }
- }
-
- if (!Objects.equals(mMissing, other.mMissing)) {
- return false;
- }
-
- if (mPermissionReviewRequired == null) {
- if (other.mPermissionReviewRequired != null) {
- return false;
- }
- } else if (!mPermissionReviewRequired.equals(other.mPermissionReviewRequired)) {
- return false;
- }
- return Arrays.equals(mGlobalGids, other.mGlobalGids);
- }
-
- /**
- * Check whether the permissions state is missing for a user. This can happen if permission
- * state is rolled back and we'll need to generate a reasonable default state to keep the app
- * usable.
- */
- public boolean isMissing(@UserIdInt int userId) {
- return mMissing != null && mMissing.get(userId);
- }
-
- /**
- * Set whether the permissions state is missing for a user. This can happen if permission state
- * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
- */
- public void setMissing(boolean missing, @UserIdInt int userId) {
- if (missing) {
- if (mMissing == null) {
- mMissing = new SparseBooleanArray();
- }
- mMissing.put(userId, true);
- } else {
- if (mMissing != null) {
- mMissing.delete(userId);
- if (mMissing.size() == 0) {
- mMissing = null;
- }
- }
- }
- }
-
- public boolean isPermissionReviewRequired(int userId) {
- return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId);
- }
-
- /**
- * Grant an install permission.
- *
- * @param permission The permission to grant.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
- */
- public int grantInstallPermission(BasePermission permission) {
- return grantPermission(permission, UserHandle.USER_ALL);
- }
-
- /**
- * Revoke an install permission.
- *
- * @param permission The permission to revoke.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
- */
- public int revokeInstallPermission(BasePermission permission) {
- return revokePermission(permission, UserHandle.USER_ALL);
- }
-
- /**
- * Grant a runtime permission for a given device user.
- *
- * @param permission The permission to grant.
- * @param userId The device user id.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
- */
- public int grantRuntimePermission(BasePermission permission, int userId) {
- enforceValidUserId(userId);
- if (userId == UserHandle.USER_ALL) {
- return PERMISSION_OPERATION_FAILURE;
- }
- return grantPermission(permission, userId);
- }
-
- /**
- * Revoke a runtime permission for a given device user.
- *
- * @param permission The permission to revoke.
- * @param userId The device user id.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
- * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
- * #PERMISSION_OPERATION_FAILURE}.
- */
- public int revokeRuntimePermission(BasePermission permission, int userId) {
- enforceValidUserId(userId);
- if (userId == UserHandle.USER_ALL) {
- return PERMISSION_OPERATION_FAILURE;
- }
- return revokePermission(permission, userId);
- }
-
- /**
- * Gets whether this state has a given runtime permission for a
- * given device user id.
- *
- * @param name The permission name.
- * @param userId The device user id.
- * @return Whether this state has the permission.
- */
- public boolean hasRuntimePermission(String name, int userId) {
- enforceValidUserId(userId);
- return !hasInstallPermission(name) && hasPermission(name, userId);
- }
-
- /**
- * Gets whether this state has a given install permission.
- *
- * @param name The permission name.
- * @return Whether this state has the permission.
- */
- public boolean hasInstallPermission(String name) {
- return hasPermission(name, UserHandle.USER_ALL);
- }
-
- /**
- * Gets whether the state has a given permission for the specified
- * user, regardless if this is an install or a runtime permission.
- *
- * @param name The permission name.
- * @param userId The device user id.
- * @return Whether the user has the permission.
- */
- public boolean hasPermission(String name, int userId) {
- enforceValidUserId(userId);
-
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- PermissionData permissionData = mPermissions.get(name);
-
- return permissionData != null && permissionData.isGranted(userId);
- }
-
- }
-
- /**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
- */
- public boolean hasRequestedPermission(ArraySet<String> names) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- for (int i=names.size()-1; i>=0; i--) {
- if (mPermissions.get(names.valueAt(i)) != null) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
- */
- public boolean hasRequestedPermission(String name) {
- return mPermissions != null && (mPermissions.get(name) != null);
- }
- /**
- * Gets all permissions for a given device user id regardless if they
- * are install time or runtime permissions.
- *
- * @param userId The device user id.
- * @return The permissions or an empty set.
- */
- public Set<String> getPermissions(int userId) {
- enforceValidUserId(userId);
-
- synchronized (mLock) {
- if (mPermissions == null) {
- return Collections.emptySet();
- }
-
- Set<String> permissions = new ArraySet<>(mPermissions.size());
-
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String permission = mPermissions.keyAt(i);
-
- if (hasInstallPermission(permission)) {
- permissions.add(permission);
- continue;
- }
-
- if (userId != UserHandle.USER_ALL) {
- if (hasRuntimePermission(permission, userId)) {
- permissions.add(permission);
- }
- }
- }
-
- return permissions;
- }
- }
-
- /**
- * Gets the state for an install permission or null if no such.
- *
- * @param name The permission name.
- * @return The permission state.
- */
- public PermissionState getInstallPermissionState(String name) {
- return getPermissionState(name, UserHandle.USER_ALL);
- }
-
- /**
- * Gets the state for a runtime permission or null if no such.
- *
- * @param name The permission name.
- * @param userId The device user id.
- * @return The permission state.
- */
- public PermissionState getRuntimePermissionState(String name, int userId) {
- enforceValidUserId(userId);
- return getPermissionState(name, userId);
- }
-
- /**
- * Gets all install permission states.
- *
- * @return The permission states or an empty set.
- */
- public List<PermissionState> getInstallPermissionStates() {
- return getPermissionStatesInternal(UserHandle.USER_ALL);
- }
-
- /**
- * Gets all runtime permission states.
- *
- * @return The permission states or an empty set.
- */
- public List<PermissionState> getRuntimePermissionStates(int userId) {
- enforceValidUserId(userId);
- return getPermissionStatesInternal(userId);
- }
-
- /**
- * Gets the flags for a permission regardless if it is install or
- * runtime permission.
- *
- * @param name The permission name.
- * @return The permission state or null if no such.
- */
- public int getPermissionFlags(String name, int userId) {
- PermissionState installPermState = getInstallPermissionState(name);
- if (installPermState != null) {
- return installPermState.getFlags();
- }
- PermissionState runtimePermState = getRuntimePermissionState(name, userId);
- if (runtimePermState != null) {
- return runtimePermState.getFlags();
- }
- return 0;
- }
-
- /**
- * Update the flags associated with a given permission.
- * @param permission The permission whose flags to update.
- * @param userId The user for which to update.
- * @param flagMask Mask for which flags to change.
- * @param flagValues New values for the mask flags.
- * @return Whether the permission flags changed.
- */
- public boolean updatePermissionFlags(BasePermission permission, int userId,
- int flagMask, int flagValues) {
- enforceValidUserId(userId);
-
- final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
-
- synchronized (mLock) {
- if (mPermissions == null) {
- if (!mayChangeFlags) {
- return false;
- }
- ensurePermissionData(permission);
- }
- }
-
- PermissionData permissionData = null;
- synchronized (mLock) {
- permissionData = mPermissions.get(permission.getName());
- }
-
- if (permissionData == null) {
- if (!mayChangeFlags) {
- return false;
- }
- permissionData = ensurePermissionData(permission);
- }
-
- final int oldFlags = permissionData.getFlags(userId);
-
- final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues);
- if (updated) {
- final int newFlags = permissionData.getFlags(userId);
- if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
- && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- if (mPermissionReviewRequired == null) {
- mPermissionReviewRequired = new SparseBooleanArray();
- }
- mPermissionReviewRequired.put(userId, true);
- } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
- && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
- if (mPermissionReviewRequired != null && !hasPermissionRequiringReview(userId)) {
- mPermissionReviewRequired.delete(userId);
- if (mPermissionReviewRequired.size() <= 0) {
- mPermissionReviewRequired = null;
- }
- }
- }
- }
- return updated;
- }
-
- private boolean hasPermissionRequiringReview(int userId) {
- synchronized (mLock) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- final PermissionData permission = mPermissions.valueAt(i);
- if ((permission.getFlags(userId)
- & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- public boolean updatePermissionFlagsForAllPermissions(
- int userId, int flagMask, int flagValues) {
- enforceValidUserId(userId);
-
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- boolean changed = false;
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionData permissionData = mPermissions.valueAt(i);
- changed |= permissionData.updateFlags(userId, flagMask, flagValues);
- }
-
- return changed;
- }
- }
-
- /**
- * Compute the Linux gids for a given device user from the permissions
- * granted to this user. Note that these are computed to avoid additional
- * state as they are rarely accessed.
- *
- * @param userId The device user id.
- * @return The gids for the device user.
- */
- public int[] computeGids(int userId) {
- enforceValidUserId(userId);
-
- int[] gids = mGlobalGids;
-
- synchronized (mLock) {
- if (mPermissions != null) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- String permission = mPermissions.keyAt(i);
- if (!hasPermission(permission, userId)) {
- continue;
- }
- PermissionData permissionData = mPermissions.valueAt(i);
- final int[] permGids = permissionData.computeGids(userId);
- if (permGids != NO_GIDS) {
- gids = appendInts(gids, permGids);
- }
- }
- }
- }
-
- return gids;
- }
-
- /**
- * Compute the Linux gids for all device users from the permissions
- * granted to these users.
- *
- * @return The gids for all device users.
- */
- public int[] computeGids(int[] userIds) {
- int[] gids = mGlobalGids;
-
- for (int userId : userIds) {
- final int[] userGids = computeGids(userId);
- gids = appendInts(gids, userGids);
- }
-
- return gids;
- }
-
- /**
- * Resets the internal state of this object.
- */
- public void reset() {
- mGlobalGids = NO_GIDS;
-
- synchronized (mLock) {
- mPermissions = null;
- invalidateCache();
- }
-
- mMissing = null;
- mPermissionReviewRequired = null;
- }
-
- private PermissionState getPermissionState(String name, int userId) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return null;
- }
- PermissionData permissionData = mPermissions.get(name);
- if (permissionData == null) {
- return null;
- }
-
- return permissionData.getPermissionState(userId);
- }
- }
-
- private List<PermissionState> getPermissionStatesInternal(int userId) {
- enforceValidUserId(userId);
-
- synchronized (mLock) {
- if (mPermissions == null) {
- return Collections.emptyList();
- }
-
- List<PermissionState> permissionStates = new ArrayList<>();
-
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionData permissionData = mPermissions.valueAt(i);
-
- PermissionState permissionState = permissionData.getPermissionState(userId);
- if (permissionState != null) {
- permissionStates.add(permissionState);
- }
- }
-
- return permissionStates;
- }
- }
-
- private int grantPermission(BasePermission permission, int userId) {
- if (hasPermission(permission.getName(), userId)) {
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
- final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
-
- PermissionData permissionData = ensurePermissionData(permission);
-
- if (!permissionData.grant(userId)) {
- return PERMISSION_OPERATION_FAILURE;
- }
-
- if (hasGids) {
- final int[] newGids = computeGids(userId);
- if (oldGids.length != newGids.length) {
- return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
- }
- }
-
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- private int revokePermission(BasePermission permission, int userId) {
- final String permName = permission.getName();
- if (!hasPermission(permName, userId)) {
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
- final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
-
- PermissionData permissionData = null;
- synchronized (mLock) {
- permissionData = mPermissions.get(permName);
- }
-
- if (!permissionData.revoke(userId)) {
- return PERMISSION_OPERATION_FAILURE;
- }
-
- if (permissionData.isDefault()) {
- ensureNoPermissionData(permName);
- }
-
- if (hasGids) {
- final int[] newGids = computeGids(userId);
- if (oldGids.length != newGids.length) {
- return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
- }
- }
-
- return PERMISSION_OPERATION_SUCCESS;
- }
-
- // TODO: fix this to use arraycopy and append all ints in one go
- private static int[] appendInts(int[] current, int[] added) {
- if (current != null && added != null) {
- for (int guid : added) {
- current = ArrayUtils.appendInt(current, guid);
- }
- }
- return current;
- }
-
- private static void enforceValidUserId(int userId) {
- if (userId != UserHandle.USER_ALL && userId < 0) {
- throw new IllegalArgumentException("Invalid userId:" + userId);
- }
- }
-
- private PermissionData ensurePermissionData(BasePermission permission) {
- final String permName = permission.getName();
-
- synchronized (mLock) {
- if (mPermissions == null) {
- mPermissions = new ArrayMap<>();
- }
- PermissionData permissionData = mPermissions.get(permName);
- if (permissionData == null) {
- permissionData = new PermissionData(permission);
- mPermissions.put(permName, permissionData);
- }
- return permissionData;
- }
-
- }
-
- private void ensureNoPermissionData(String name) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return;
- }
- mPermissions.remove(name);
- if (mPermissions.isEmpty()) {
- mPermissions = null;
- }
- }
-
- }
-
- private static final class PermissionData {
-
- private final Object mLock = new Object();
-
- private final BasePermission mPerm;
- @GuardedBy("mLock")
- private SparseArray<PermissionState> mUserStates = new SparseArray<>();
-
- public PermissionData(BasePermission perm) {
- mPerm = perm;
- }
-
- public PermissionData(PermissionData other) {
- this(other.mPerm);
-
- synchronized (mLock) {
- final int otherStateCount = other.mUserStates.size();
- for (int i = 0; i < otherStateCount; i++) {
- final int otherUserId = other.mUserStates.keyAt(i);
- PermissionState otherState = other.mUserStates.valueAt(i);
- mUserStates.put(otherUserId, new PermissionState(otherState));
- }
- }
- }
-
- public int[] computeGids(int userId) {
- return mPerm.computeGids(userId);
- }
-
- public boolean isGranted(int userId) {
- synchronized (mLock) {
- if (isInstallPermission()) {
- userId = UserHandle.USER_ALL;
- }
-
- PermissionState userState = mUserStates.get(userId);
- if (userState == null) {
- return false;
- }
-
- return userState.mGranted;
- }
- }
-
- public boolean grant(int userId) {
- synchronized (mLock) {
- if (!isCompatibleUserId(userId)) {
- return false;
- }
-
- if (isGranted(userId)) {
- return false;
- }
-
- PermissionState userState = mUserStates.get(userId);
- if (userState == null) {
- userState = new PermissionState(mPerm);
- mUserStates.put(userId, userState);
- }
-
- userState.mGranted = true;
-
- invalidateCache();
- return true;
- }
- }
-
- public boolean revoke(int userId) {
- synchronized (mLock) {
- if (!isCompatibleUserId(userId)) {
- return false;
- }
-
- if (!isGranted(userId)) {
- return false;
- }
-
- PermissionState userState = mUserStates.get(userId);
- userState.mGranted = false;
-
- if (userState.isDefault()) {
- mUserStates.remove(userId);
- }
-
- invalidateCache();
- return true;
- }
- }
-
- public PermissionState getPermissionState(int userId) {
- synchronized (mLock) {
- return mUserStates.get(userId);
- }
- }
-
- public int getFlags(int userId) {
- synchronized (mLock) {
- PermissionState userState = mUserStates.get(userId);
- if (userState != null) {
- return userState.mFlags;
- }
- return 0;
- }
- }
-
- public boolean isDefault() {
- synchronized (mLock) {
- return mUserStates.size() <= 0;
- }
- }
-
- public static boolean isInstallPermissionKey(int userId) {
- return userId == UserHandle.USER_ALL;
- }
-
- public boolean updateFlags(int userId, int flagMask, int flagValues) {
- synchronized (mLock) {
- if (isInstallPermission()) {
- userId = UserHandle.USER_ALL;
- }
-
- if (!isCompatibleUserId(userId)) {
- return false;
- }
-
- final int newFlags = flagValues & flagMask;
-
- // Okay to do before the modification because we hold the lock.
- invalidateCache();
-
- PermissionState userState = mUserStates.get(userId);
- if (userState != null) {
- final int oldFlags = userState.mFlags;
- userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
- if (userState.isDefault()) {
- mUserStates.remove(userId);
- }
- return userState.mFlags != oldFlags;
- } else if (newFlags != 0) {
- userState = new PermissionState(mPerm);
- userState.mFlags = newFlags;
- mUserStates.put(userId, userState);
- return true;
- }
-
- return false;
- }
- }
-
- private boolean isCompatibleUserId(int userId) {
- return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
- }
-
- private boolean isInstallPermission() {
- return mUserStates.size() == 1
- && mUserStates.get(UserHandle.USER_ALL) != null;
- }
- }
-
- public static final class PermissionState {
- private final BasePermission mPermission;
- private boolean mGranted;
- private int mFlags;
-
- public PermissionState(BasePermission permission) {
- mPermission = permission;
- }
-
- public PermissionState(PermissionState other) {
- mPermission = other.mPermission;
- mGranted = other.mGranted;
- mFlags = other.mFlags;
- }
-
- public boolean isDefault() {
- return !mGranted && mFlags == 0;
- }
-
- public BasePermission getPermission() {
- return mPermission;
- }
-
- public String getName() {
- return mPermission.getName();
- }
-
- public boolean isGranted() {
- return mGranted;
- }
-
- public int getFlags() {
- return mFlags;
- }
- }
-}
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index 65dc320..c0d71ac 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -18,6 +18,14 @@
]
},
{
+ "name": "CtsAppSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.AppSecurityTests#rebootWithDuplicatePermission"
+ }
+ ]
+ },
+ {
"name": "CtsPermission2TestCases",
"options": [
{
@@ -29,17 +37,6 @@
]
},
{
- "name": "CtsPermissionHostTestCases"
- },
- {
- "name": "CtsAppSecurityHostTestCases",
- "options": [
- {
- "include-filter": "android.appsecurity.cts.AppSecurityTests#rebootWithDuplicatePermission"
- }
- ]
- },
- {
"name": "CtsStatsdHostTestCases",
"options": [
{
diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
index 4c047ff..b45176b 100644
--- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -207,23 +207,6 @@
}
/**
- * Gets whether the state has a given install permission.
- *
- * @param name The permission name.
- * @return Whether the state has the install permission.
- */
- public boolean hasInstallPermission(@NonNull String name) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- PermissionState permissionState = mPermissions.get(name);
- return permissionState != null && permissionState.isGranted()
- && !permissionState.isRuntime();
- }
- }
-
- /**
* Returns whether the state has any known request for the given permission name,
* whether or not it has been granted.
*
@@ -459,11 +442,11 @@
/**
* Put a permission state.
*/
- public void putPermissionState(@NonNull BasePermission permission, boolean isRuntime,
- boolean isGranted, int flags) {
+ public void putPermissionState(@NonNull BasePermission permission, boolean isGranted,
+ int flags) {
synchronized (mLock) {
ensureNoPermissionState(permission.name);
- PermissionState permissionState = ensurePermissionState(permission, isRuntime);
+ PermissionState permissionState = ensurePermissionState(permission);
if (isGranted) {
permissionState.grant();
}
@@ -540,12 +523,6 @@
@NonNull
private PermissionState ensurePermissionState(@NonNull BasePermission permission) {
- return ensurePermissionState(permission, permission.isRuntime());
- }
-
- @NonNull
- private PermissionState ensurePermissionState(@NonNull BasePermission permission,
- boolean isRuntime) {
final String permissionName = permission.getName();
synchronized (mLock) {
if (mPermissions == null) {
@@ -553,7 +530,7 @@
}
PermissionState permissionState = mPermissions.get(permissionName);
if (permissionState == null) {
- permissionState = new PermissionState(permission, isRuntime);
+ permissionState = new PermissionState(permission);
mPermissions.put(permissionName, permissionState);
}
return permissionState;
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 1d31285..de8823c 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -40,7 +40,10 @@
import android.media.soundtrigger_middleware.SoundModelType;
import android.media.soundtrigger_middleware.SoundTriggerModuleProperties;
import android.os.HidlMemoryUtil;
+import android.os.ParcelFileDescriptor;
+import java.io.FileDescriptor;
+import java.io.IOException;
import java.util.regex.Matcher;
/**
@@ -196,8 +199,18 @@
hidlModel.header.type = aidl2hidlSoundModelType(aidlModel.type);
hidlModel.header.uuid = aidl2hidlUuid(aidlModel.uuid);
hidlModel.header.vendorUuid = aidl2hidlUuid(aidlModel.vendorUuid);
- hidlModel.data = HidlMemoryUtil.fileDescriptorToHidlMemory(aidlModel.data,
- aidlModel.dataSize);
+
+ // Extract a dup of the underlying FileDescriptor out of aidlModel.data without changing
+ // the original.
+ FileDescriptor fd = new FileDescriptor();
+ try {
+ ParcelFileDescriptor dup = aidlModel.data.dup();
+ fd.setInt$(dup.detachFd());
+ hidlModel.data = HidlMemoryUtil.fileDescriptorToHidlMemory(fd, aidlModel.dataSize);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
return hidlModel;
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 740b592..6792430 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4770,14 +4770,14 @@
supportsEnterPipOnTaskSwitch = false;
break;
case RESUMED:
- // Do nothing if currently in the process of resuming the activity. Otherwise,
- // starting to pause it since it is not visible.
- if (!mSetToSleep) {
+ // If the app is capable of entering PIP, we should try pausing it now
+ // so it can PIP correctly.
+ if (deferHidingClient) {
+ getRootTask().startPausingLocked(
+ mStackSupervisor.mUserLeaving /* userLeaving */,
+ false /* uiSleeping */, null /* resuming */, "makeInvisible");
break;
}
- getRootTask().startPausingLocked(mStackSupervisor.mUserLeaving,
- false /* uiSleeping */, null /* resuming */, "makeInvisible");
- // fall through
case INITIALIZING:
case PAUSING:
case PAUSED:
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index ab46450..f7fd6ec 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -731,6 +731,11 @@
final Task rootTask = task.getRootTask();
beginDeferResume();
+ // The LaunchActivityItem also contains process configuration, so the configuration change
+ // from WindowProcessController#setProcess can be deferred. The major reason is that if
+ // the activity has FixedRotationAdjustments, it needs to be applied with configuration.
+ // In general, this reduces a binder transaction if process configuration is changed.
+ proc.pauseConfigurationDispatch();
try {
r.startFreezingScreenLocked(proc, 0);
@@ -824,9 +829,9 @@
// Because we could be starting an Activity in the system process this may not go
// across a Binder interface which would create a new Configuration. Consequently
// we have to always create a new Configuration here.
-
+ final Configuration procConfig = proc.prepareConfigurationForLaunchingActivity();
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- proc.getConfiguration(), r.getMergedOverrideConfiguration());
+ procConfig, r.getMergedOverrideConfiguration());
r.setLastReportedConfiguration(mergedConfiguration);
logIfTransactionTooLarge(r.intent, r.getSavedState());
@@ -860,6 +865,11 @@
// Schedule transaction.
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
+ if (procConfig.seq > mRootWindowContainer.getConfiguration().seq) {
+ // If the seq is increased, there should be something changed (e.g. registered
+ // activity configuration).
+ proc.setLastReportedConfiguration(procConfig);
+ }
if ((proc.mInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0
&& mService.mHasHeavyWeightFeature) {
// This may be a heavy-weight process! Note that the package manager will ensure
@@ -894,6 +904,7 @@
}
} finally {
endDeferResume();
+ proc.resumeConfigurationDispatch();
}
r.launchFailed = false;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e8e4059..fa4373f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -700,11 +700,15 @@
mService.updateConfigurationLocked(mRequest.globalConfig, null, false);
}
+ // The original options may have additional info about metrics. The mOptions is not
+ // used here because it may be cleared in setTargetStackIfNeeded.
+ final ActivityOptions originalOptions = mRequest.activityOptions != null
+ ? mRequest.activityOptions.getOriginalOptions() : null;
// Notify ActivityMetricsLogger that the activity has launched.
// ActivityMetricsLogger will then wait for the windows to be drawn and populate
// WaitResult.
mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, res,
- mLastStartActivityRecord, mOptions);
+ mLastStartActivityRecord, originalOptions);
return getExternalResult(mRequest.waitResult == null ? res
: waitForResult(res, mLastStartActivityRecord));
}
@@ -1493,9 +1497,10 @@
// anyone interested in this piece of information.
final Task homeStack = targetTask.getDisplayArea().getRootHomeTask();
final boolean homeTaskVisible = homeStack != null && homeStack.shouldBeVisible(null);
+ final ActivityRecord top = targetTask.getTopNonFinishingActivity();
+ final boolean visible = top != null && top.isVisible();
mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
- targetTask.getTaskInfo(), homeTaskVisible, clearedTask,
- targetTask.getTopNonFinishingActivity().isVisible());
+ targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible);
}
}
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 6305fda..958a7a8 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -16,9 +16,13 @@
package com.android.server.wm;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
+
import android.util.ArrayMap;
import android.util.ArraySet;
+import com.android.internal.protolog.common.ProtoLog;
+
import java.util.Set;
/**
@@ -63,25 +67,38 @@
private void tryFinish() {
if (mRemainingTransactions == 0 && mReady) {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Finished. Reporting %d "
+ + "containers to %s", BLASTSyncEngine.this.hashCode(), mSyncId,
+ mWindowContainersReady.size(), mListener);
mListener.onTransactionReady(mSyncId, mWindowContainersReady);
mPendingSyncs.remove(mSyncId);
}
}
- public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
+ public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
mRemainingTransactions--;
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Child ready, now ready=%b"
+ + " and waiting on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId,
+ mReady, mRemainingTransactions);
mWindowContainersReady.addAll(windowContainersReady);
tryFinish();
}
void setReady() {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Set ready",
+ BLASTSyncEngine.this.hashCode(), mSyncId);
mReady = true;
tryFinish();
}
boolean addToSync(WindowContainer wc) {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Trying to add %s",
+ BLASTSyncEngine.this.hashCode(), mSyncId, wc);
if (wc.prepareForSync(this, mSyncId)) {
mRemainingTransactions++;
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Added %s. now waiting "
+ + "on %d transactions", BLASTSyncEngine.this.hashCode(), mSyncId, wc,
+ mRemainingTransactions);
return true;
}
return false;
@@ -105,6 +122,7 @@
final int id = mNextSyncId++;
final SyncState s = new SyncState(listener, id);
mPendingSyncs.put(id, s);
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncSet{%x:%d} Start for %s", hashCode(), id, listener);
return id;
}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 01c007e..dd92f507 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -52,10 +52,7 @@
public void binderDied() {
synchronized (mGlobalLock) {
mOrganizersByFeatureIds.remove(mFeature);
- mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
- if (da.mOrganizer != mOrganizer) return;
- da.setOrganizer(null);
- });
+ removeOrganizer(mOrganizer);
}
}
}
@@ -112,11 +109,7 @@
organizer.asBinder(), uid);
mOrganizersByFeatureIds.entrySet().removeIf(
entry -> entry.getValue().asBinder() == organizer.asBinder());
-
- mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
- if (da.mOrganizer != organizer) return;
- da.setOrganizer(null);
- });
+ removeOrganizer(organizer);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -151,4 +144,13 @@
// Oh well...
}
}
+
+ private void removeOrganizer(IDisplayAreaOrganizer organizer) {
+ IBinder organizerBinder = organizer.asBinder();
+ mService.mRootWindowContainer.forAllDisplayAreas((da) -> {
+ if (da.mOrganizer != null && da.mOrganizer.asBinder().equals(organizerBinder)) {
+ da.setOrganizer(null);
+ }
+ });
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0be5cbe..b3e69d4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1849,7 +1849,8 @@
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
- outConfig.windowConfiguration.getBounds().set(0, 0, dw, dh);
+ outConfig.windowConfiguration.setMaxBounds(0, 0, dw, dh);
+ outConfig.windowConfiguration.setBounds(outConfig.windowConfiguration.getMaxBounds());
final int uiMode = getConfiguration().uiMode;
final DisplayCutout displayCutout =
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 572c9b3..e54da9e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -29,6 +29,8 @@
import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
@@ -215,7 +217,8 @@
/** Use the transit animation in style resource (see {@link #selectAnimation}). */
static final int ANIMATION_STYLEABLE = 0;
- private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR};
+ private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR,
+ ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR};
private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR};
private final WindowManagerService mService;
@@ -301,6 +304,16 @@
private WindowState mNavigationBarAlt = null;
@WindowManagerPolicy.AltBarPosition
private int mNavigationBarAltPosition = ALT_BAR_UNKNOWN;
+ // Alternative climate bar for when flexible insets mapping is used to place a climate bar on
+ // the screen.
+ private WindowState mClimateBarAlt = null;
+ @WindowManagerPolicy.AltBarPosition
+ private int mClimateBarAltPosition = ALT_BAR_UNKNOWN;
+ // Alternative extra nav bar for when flexible insets mapping is used to place an extra nav bar
+ // on the screen.
+ private WindowState mExtraNavBarAlt = null;
+ @WindowManagerPolicy.AltBarPosition
+ private int mExtraNavBarAltPosition = ALT_BAR_UNKNOWN;
/** See {@link #getNavigationBarFrameHeight} */
private int[] mNavigationBarFrameHeightForRotationDefault = new int[4];
@@ -649,7 +662,7 @@
mRefreshRatePolicy = new RefreshRatePolicy(mService,
mDisplayContent.getDisplayInfo(),
- mService.mHighRefreshRateBlacklist);
+ mService.mHighRefreshRateDenylist);
mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(mHandler,
mContext, () -> {
@@ -669,6 +682,12 @@
if (mNavigationBarAlt != null && mNavigationBarAltPosition == pos) {
requestTransientBars(mNavigationBarAlt);
}
+ if (mClimateBarAlt != null && mClimateBarAltPosition == pos) {
+ requestTransientBars(mClimateBarAlt);
+ }
+ if (mExtraNavBarAlt != null && mExtraNavBarAltPosition == pos) {
+ requestTransientBars(mExtraNavBarAlt);
+ }
}
void systemReady() {
@@ -936,6 +955,12 @@
if (mNavigationBarAlt == win) {
mNavigationBarAltPosition = getAltBarPosition(attrs);
}
+ if (mClimateBarAlt == win) {
+ mClimateBarAltPosition = getAltBarPosition(attrs);
+ }
+ if (mExtraNavBarAlt == win) {
+ mExtraNavBarAltPosition = getAltBarPosition(attrs);
+ }
}
/**
@@ -1033,6 +1058,16 @@
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
}
break;
+ case ITYPE_CLIMATE_BAR:
+ if (mClimateBarAlt != null && mClimateBarAlt.isAlive()) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ break;
+ case ITYPE_EXTRA_NAVIGATION_BAR:
+ if (mExtraNavBarAlt != null && mExtraNavBarAlt.isAlive()) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ break;
}
}
}
@@ -1146,6 +1181,14 @@
mNavigationBarAlt = win;
mNavigationBarAltPosition = getAltBarPosition(attrs);
break;
+ case ITYPE_CLIMATE_BAR:
+ mClimateBarAlt = win;
+ mClimateBarAltPosition = getAltBarPosition(attrs);
+ break;
+ case ITYPE_EXTRA_NAVIGATION_BAR:
+ mExtraNavBarAlt = win;
+ mExtraNavBarAltPosition = getAltBarPosition(attrs);
+ break;
}
mDisplayContent.setInsetProvider(insetsType, win, null);
}
@@ -1194,6 +1237,8 @@
switch (insetsType) {
case ITYPE_NAVIGATION_BAR:
case ITYPE_STATUS_BAR:
+ case ITYPE_CLIMATE_BAR:
+ case ITYPE_EXTRA_NAVIGATION_BAR:
case ITYPE_CAPTION_BAR:
if (++count > 1) {
throw new IllegalArgumentException(
@@ -1223,6 +1268,12 @@
if (mDisplayContent.isDefaultDisplay) {
mService.mPolicy.setKeyguardCandidateLw(null);
}
+ } else if (mClimateBarAlt == win) {
+ mClimateBarAlt = null;
+ mDisplayContent.setInsetProvider(ITYPE_CLIMATE_BAR, null, null);
+ } else if (mExtraNavBarAlt == win) {
+ mExtraNavBarAlt = null;
+ mDisplayContent.setInsetProvider(ITYPE_EXTRA_NAVIGATION_BAR, null, null);
}
if (mLastFocusedWindow == win) {
mLastFocusedWindow = null;
@@ -1311,7 +1362,8 @@
return R.anim.dock_left_enter;
}
}
- } else if (win == mStatusBarAlt || win == mNavigationBarAlt) {
+ } else if (win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt
+ || win == mExtraNavBarAlt) {
if (win.getAttrs().windowAnimations != 0) {
return ANIMATION_STYLEABLE;
}
@@ -2810,10 +2862,19 @@
}
final InsetsState requestedState = controlTarget.getRequestedInsetsState();
- final @InsetsType int restorePositionTypes = (requestedState.getSourceOrDefaultVisibility(
- ITYPE_NAVIGATION_BAR) ? Type.navigationBars() : 0) | (
- requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) ? Type.statusBars()
- : 0);
+ final @InsetsType int restorePositionTypes =
+ (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)
+ ? Type.navigationBars() : 0)
+ | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)
+ ? Type.statusBars() : 0)
+ | (mExtraNavBarAlt != null
+ && requestedState.getSourceOrDefaultVisibility(
+ ITYPE_EXTRA_NAVIGATION_BAR)
+ ? Type.navigationBars() : 0)
+ | (mClimateBarAlt != null
+ && requestedState.getSourceOrDefaultVisibility(
+ ITYPE_CLIMATE_BAR)
+ ? Type.statusBars() : 0);
if (swipeTarget == mNavigationBar
&& (restorePositionTypes & Type.navigationBars()) != 0) {
@@ -3326,6 +3387,16 @@
pw.print(prefix); pw.print("mNavigationBarAltPosition=");
pw.println(mNavigationBarAltPosition);
}
+ if (mClimateBarAlt != null) {
+ pw.print(prefix); pw.print("mClimateBarAlt="); pw.println(mClimateBarAlt);
+ pw.print(prefix); pw.print("mClimateBarAltPosition=");
+ pw.println(mClimateBarAltPosition);
+ }
+ if (mExtraNavBarAlt != null) {
+ pw.print(prefix); pw.print("mExtraNavBarAlt="); pw.println(mExtraNavBarAlt);
+ pw.print(prefix); pw.print("mExtraNavBarAltPosition=");
+ pw.println(mExtraNavBarAltPosition);
+ }
if (mFocusedWindow != null) {
pw.print(prefix); pw.print("mFocusedWindow="); pw.println(mFocusedWindow);
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 6e32d0e..b80ed6b 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -258,16 +258,13 @@
}
class InputInterceptor {
- InputChannel mServerChannel, mClientChannel;
+ InputChannel mClientChannel;
DragInputEventReceiver mInputEventReceiver;
InputApplicationHandle mDragApplicationHandle;
InputWindowHandle mDragWindowHandle;
InputInterceptor(Display display) {
- InputChannel[] channels = InputChannel.openInputChannelPair("drag");
- mServerChannel = channels[0];
- mClientChannel = channels[1];
- mService.mInputManager.registerInputChannel(mServerChannel);
+ mClientChannel = mService.mInputManager.createInputChannel("drag");
mInputEventReceiver = new DragInputEventReceiver(mClientChannel,
mService.mH.getLooper(), mDragDropController);
@@ -278,7 +275,7 @@
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
display.getDisplayId());
mDragWindowHandle.name = "drag";
- mDragWindowHandle.token = mServerChannel.getToken();
+ mDragWindowHandle.token = mClientChannel.getToken();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -308,13 +305,11 @@
}
void tearDown() {
- mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
+ mService.mInputManager.removeInputChannel(mClientChannel.getToken());
mInputEventReceiver.dispose();
mInputEventReceiver = null;
mClientChannel.dispose();
- mServerChannel.dispose();
mClientChannel = null;
- mServerChannel = null;
mDragWindowHandle = null;
mDragApplicationHandle = null;
@@ -326,7 +321,7 @@
}
InputChannel getInputChannel() {
- return mInputInterceptor == null ? null : mInputInterceptor.mServerChannel;
+ return mInputInterceptor == null ? null : mInputInterceptor.mClientChannel;
}
InputWindowHandle getInputWindowHandle() {
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 2a5bf16..3b89a24 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -184,23 +184,13 @@
InputChannel openInputChannel() {
final String name = getName();
-
- final InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- mInputChannel = inputChannels[0];
- final InputChannel clientChannel = inputChannels[1];
- mWmService.mInputManager.registerInputChannel(mInputChannel);
-
- if (mInputChannel.getToken() != clientChannel.getToken()) {
- throw new IllegalStateException("Client and Server tokens are expected to"
- + "be the same");
- }
-
- return clientChannel;
+ mInputChannel = mWmService.mInputManager.createInputChannel(name);
+ return mInputChannel;
}
void onRemoved() {
if (mInputChannel != null) {
- mWmService.mInputManager.unregisterInputChannel(mInputChannel.getToken());
+ mWmService.mInputManager.removeInputChannel(mInputChannel.getToken());
mInputChannel.dispose();
mInputChannel = null;
}
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
similarity index 74%
rename from services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
rename to services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
index aac6b25..cdc14cd 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateBlacklist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
@@ -34,62 +34,62 @@
/**
* A Denylist for packages that should force the display out of high refresh rate.
*/
-class HighRefreshRateBlacklist {
+class HighRefreshRateDenylist {
- private final ArraySet<String> mBlacklistedPackages = new ArraySet<>();
+ private final ArraySet<String> mDenylistedPackages = new ArraySet<>();
@NonNull
- private final String[] mDefaultBlacklist;
+ private final String[] mDefaultDenylist;
private final Object mLock = new Object();
private DeviceConfigInterface mDeviceConfig;
private OnPropertiesChangedListener mListener = new OnPropertiesChangedListener();
- static HighRefreshRateBlacklist create(@NonNull Resources r) {
- return new HighRefreshRateBlacklist(r, DeviceConfigInterface.REAL);
+ static HighRefreshRateDenylist create(@NonNull Resources r) {
+ return new HighRefreshRateDenylist(r, DeviceConfigInterface.REAL);
}
@VisibleForTesting
- HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) {
- mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist);
+ HighRefreshRateDenylist(Resources r, DeviceConfigInterface deviceConfig) {
+ mDefaultDenylist = r.getStringArray(R.array.config_highRefreshRateBlacklist);
mDeviceConfig = deviceConfig;
mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
BackgroundThread.getExecutor(), mListener);
final String property = mDeviceConfig.getProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
KEY_HIGH_REFRESH_RATE_BLACKLIST);
- updateBlacklist(property);
+ updateDenylist(property);
}
- private void updateBlacklist(@Nullable String property) {
+ private void updateDenylist(@Nullable String property) {
synchronized (mLock) {
- mBlacklistedPackages.clear();
+ mDenylistedPackages.clear();
if (property != null) {
String[] packages = property.split(",");
for (String pkg : packages) {
String pkgName = pkg.trim();
if (!pkgName.isEmpty()) {
- mBlacklistedPackages.add(pkgName);
+ mDenylistedPackages.add(pkgName);
}
}
} else {
// If there's no config, or the config has been deleted, fallback to the device's
// default denylist
- for (String pkg : mDefaultBlacklist) {
- mBlacklistedPackages.add(pkg);
+ for (String pkg : mDefaultDenylist) {
+ mDenylistedPackages.add(pkg);
}
}
}
}
- boolean isBlacklisted(String packageName) {
+ boolean isDenylisted(String packageName) {
synchronized (mLock) {
- return mBlacklistedPackages.contains(packageName);
+ return mDenylistedPackages.contains(packageName);
}
}
void dump(PrintWriter pw) {
- pw.println("High Refresh Rate Blacklist");
+ pw.println("High Refresh Rate Denylist");
pw.println(" Packages:");
synchronized (mLock) {
- for (String pkg : mBlacklistedPackages) {
+ for (String pkg : mDenylistedPackages) {
pw.println(" " + pkg);
}
}
@@ -100,13 +100,13 @@
void dispose() {
mDeviceConfig.removeOnPropertiesChangedListener(mListener);
mDeviceConfig = null;
- mBlacklistedPackages.clear();
+ mDenylistedPackages.clear();
}
private class OnPropertiesChangedListener implements DeviceConfig.OnPropertiesChangedListener {
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
if (properties.getKeyset().contains(KEY_HIGH_REFRESH_RATE_BLACKLIST)) {
- updateBlacklist(
+ updateDenylist(
properties.getString(KEY_HIGH_REFRESH_RATE_BLACKLIST, null /*default*/));
}
}
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 1d1a266..edb5e85 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -35,7 +35,7 @@
class InputConsumerImpl implements IBinder.DeathRecipient {
final WindowManagerService mService;
- final InputChannel mServerChannel, mClientChannel;
+ final InputChannel mClientChannel;
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
@@ -58,16 +58,10 @@
mClientPid = clientPid;
mClientUser = clientUser;
- InputChannel[] channels = InputChannel.openInputChannelPair(name);
- mServerChannel = channels[0];
+ mClientChannel = mService.mInputManager.createInputChannel(name);
if (inputChannel != null) {
- channels[1].transferTo(inputChannel);
- channels[1].dispose();
- mClientChannel = inputChannel;
- } else {
- mClientChannel = channels[1];
+ mClientChannel.copyTo(inputChannel);
}
- mService.mInputManager.registerInputChannel(mServerChannel);
mApplicationHandle = new InputApplicationHandle(new Binder());
mApplicationHandle.name = name;
@@ -75,7 +69,7 @@
mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
mWindowHandle.name = name;
- mWindowHandle.token = mServerChannel.getToken();
+ mWindowHandle.token = mClientChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
mWindowHandle.layoutParamsFlags = 0;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -156,9 +150,8 @@
}
void disposeChannelsLw(SurfaceControl.Transaction t) {
- mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
+ mService.mInputManager.removeInputChannel(mClientChannel.getToken());
mClientChannel.dispose();
- mServerChannel.dispose();
t.remove(mInputSurface);
unlinkFromDeathRecipient();
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a7f32c0..5bed186 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,6 +16,8 @@
package com.android.server.wm;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -93,6 +95,8 @@
case ITYPE_STATUS_BAR:
case ITYPE_NAVIGATION_BAR:
case ITYPE_IME:
+ case ITYPE_CLIMATE_BAR:
+ case ITYPE_EXTRA_NAVIGATION_BAR:
mControllable = true;
break;
default:
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index c0bdbff..b59452f 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_INVALID;
@@ -42,6 +43,7 @@
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams.WindowType;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -115,7 +117,7 @@
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
- final @InternalInsetsType int type = getInsetsTypeForWindowType(attrs.type);
+ final @InternalInsetsType int type = getInsetsTypeForLayoutParams(attrs);
final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
@@ -135,7 +137,9 @@
return false;
}
- private static @InternalInsetsType int getInsetsTypeForWindowType(int type) {
+ private static @InternalInsetsType
+ int getInsetsTypeForLayoutParams(WindowManager.LayoutParams attrs) {
+ @WindowType int type = attrs.type;
switch (type) {
case TYPE_STATUS_BAR:
return ITYPE_STATUS_BAR;
@@ -143,9 +147,22 @@
return ITYPE_NAVIGATION_BAR;
case TYPE_INPUT_METHOD:
return ITYPE_IME;
- default:
- return ITYPE_INVALID;
}
+
+ // If not one of the types above, check whether an internal inset mapping is specified.
+ if (attrs.providesInsetsTypes != null) {
+ for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) {
+ switch (insetsType) {
+ case ITYPE_STATUS_BAR:
+ case ITYPE_NAVIGATION_BAR:
+ case ITYPE_CLIMATE_BAR:
+ case ITYPE_EXTRA_NAVIGATION_BAR:
+ return insetsType;
+ }
+ }
+ }
+
+ return ITYPE_INVALID;
}
/** @see #getInsetsForDispatch */
@@ -158,14 +175,15 @@
state.removeSource(type);
// Navigation bar doesn't get influenced by anything else
- if (type == ITYPE_NAVIGATION_BAR) {
+ if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) {
state.removeSource(ITYPE_IME);
state.removeSource(ITYPE_STATUS_BAR);
+ state.removeSource(ITYPE_CLIMATE_BAR);
state.removeSource(ITYPE_CAPTION_BAR);
}
// Status bar doesn't get influenced by caption bar
- if (type == ITYPE_STATUS_BAR) {
+ if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) {
state.removeSource(ITYPE_CAPTION_BAR);
}
@@ -336,8 +354,12 @@
@Nullable InsetsControlTarget fakeNavControlling) {
onControlChanged(ITYPE_STATUS_BAR, statusControlling);
onControlChanged(ITYPE_NAVIGATION_BAR, navControlling);
+ onControlChanged(ITYPE_CLIMATE_BAR, statusControlling);
+ onControlChanged(ITYPE_EXTRA_NAVIGATION_BAR, navControlling);
onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling);
onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling);
+ onControlFakeTargetChanged(ITYPE_CLIMATE_BAR, fakeStatusControlling);
+ onControlFakeTargetChanged(ITYPE_EXTRA_NAVIGATION_BAR, fakeNavControlling);
notifyPendingInsetsControlChanged();
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 4fe678d..44ce4de 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -191,7 +191,6 @@
}
private static class InputInterceptor {
- final InputChannel mServerChannel;
final InputChannel mClientChannel;
final InputWindowHandle mWindowHandle;
final InputEventReceiver mInputEventReceiver;
@@ -201,13 +200,10 @@
InputInterceptor(String namePrefix, WindowState win) {
mWmService = win.mWmService;
final String name = namePrefix + (win.mActivityRecord != null ? win.mActivityRecord : win);
- final InputChannel[] channels = InputChannel.openInputChannelPair(name);
- mServerChannel = channels[0];
- mClientChannel = channels[1];
+ mClientChannel = mWmService.mInputManager.createInputChannel(name);
mInputEventReceiver = new SimpleInputReceiver(mClientChannel);
- mWmService.mInputManager.registerInputChannel(mServerChannel);
- mToken = mServerChannel.getToken();
+ mToken = mClientChannel.getToken();
mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */,
win.getDisplayId());
@@ -239,9 +235,8 @@
}
void dispose() {
- mWmService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
+ mWmService.mInputManager.removeInputChannel(mToken);
mInputEventReceiver.dispose();
- mServerChannel.dispose();
mClientChannel.dispose();
}
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 072116f..91014aa 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -30,7 +30,7 @@
private final int mLowRefreshRateId;
private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
- private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
+ private final HighRefreshRateDenylist mHighRefreshRateDenylist;
private final WindowManagerService mWmService;
/**
@@ -55,9 +55,9 @@
static final int LAYER_PRIORITY_NOT_FOCUSED_WITH_MODE = 2;
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
- HighRefreshRateBlacklist blacklist) {
+ HighRefreshRateDenylist denylist) {
mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
- mHighRefreshRateBlacklist = blacklist;
+ mHighRefreshRateDenylist = denylist;
mWmService = wmService;
}
@@ -108,7 +108,7 @@
}
// If app is denylisted using higher refresh rate, return default (lower) refresh rate
- if (mHighRefreshRateBlacklist.isBlacklisted(packageName)) {
+ if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
return mLowRefreshRateId;
}
return 0;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d149db6..71ecf72 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -76,6 +76,7 @@
import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.Task.STACK_VISIBILITY_INVISIBLE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -1907,22 +1908,33 @@
}
boolean attachApplication(WindowProcessController app) throws RemoteException {
- final String processName = app.mName;
boolean didSomething = false;
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
- final DisplayContent display = getChildAt(displayNdx);
- final Task stack = display.getFocusedStack();
- if (stack == null) {
- continue;
- }
-
mTmpRemoteException = null;
mTmpBoolean = false; // Set to true if an activity was started.
- final PooledFunction c = PooledLambda.obtainFunction(
- RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
- PooledLambda.__(ActivityRecord.class), app, stack.topRunningActivity());
- stack.forAllActivities(c);
- c.recycle();
+ final DisplayContent display = getChildAt(displayNdx);
+ display.forAllTaskDisplayAreas(displayArea -> {
+ if (mTmpRemoteException != null) {
+ return;
+ }
+
+ for (int taskNdx = displayArea.getStackCount() - 1; taskNdx >= 0; --taskNdx) {
+ final Task rootTask = displayArea.getStackAt(taskNdx);
+ if (rootTask.getVisibility(null /*starting*/) == STACK_VISIBILITY_INVISIBLE) {
+ break;
+ }
+
+ final PooledFunction c = PooledLambda.obtainFunction(
+ RootWindowContainer::startActivityForAttachedApplicationIfNeeded, this,
+ PooledLambda.__(ActivityRecord.class), app,
+ rootTask.topRunningActivity());
+ rootTask.forAllActivities(c);
+ c.recycle();
+ if (mTmpRemoteException != null) {
+ return;
+ }
+ }
+ });
if (mTmpRemoteException != null) {
throw mTmpRemoteException;
}
@@ -1942,8 +1954,8 @@
}
try {
- if (mStackSupervisor.realStartActivityLocked(r, app, top == r /*andResume*/,
- true /*checkConfig*/)) {
+ if (mStackSupervisor.realStartActivityLocked(r, app,
+ top == r && r.isFocusable() /*andResume*/, true /*checkConfig*/)) {
mTmpBoolean = true;
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index ede6708..9205401 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -150,6 +150,14 @@
}
/**
+ * Gets the original options passed in. It should only be used for logging. DO NOT use it as a
+ * condition in the logic of activity launch.
+ */
+ ActivityOptions getOriginalOptions() {
+ return mOriginalOptions;
+ }
+
+ /**
* @see ActivityOptions#popAppVerificationBundle
*/
Bundle popAppVerificationBundle() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2a0488d..ce602de 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -318,6 +318,7 @@
// Do not move the stack as a part of reparenting
static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
+ // TODO (b/157876447): switch to Task related name
@IntDef(prefix = {"STACK_VISIBILITY"}, value = {
STACK_VISIBILITY_VISIBLE,
STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -4112,6 +4113,10 @@
return STACK_VISIBILITY_INVISIBLE;
}
+ if (isTopActivityLaunchedBehind()) {
+ return STACK_VISIBILITY_VISIBLE;
+ }
+
boolean gotSplitScreenStack = false;
boolean gotOpaqueSplitScreenPrimary = false;
boolean gotOpaqueSplitScreenSecondary = false;
@@ -4229,6 +4234,14 @@
: STACK_VISIBILITY_VISIBLE;
}
+ private boolean isTopActivityLaunchedBehind() {
+ final ActivityRecord top = topRunningActivity();
+ if (top != null && top.mLaunchTaskBehind) {
+ return true;
+ }
+ return false;
+ }
+
ActivityRecord isInTask(ActivityRecord r) {
if (r == null) {
return null;
@@ -6039,8 +6052,9 @@
// If the most recent activity was noHistory but was only stopped rather
// than stopped+finished because the device went to sleep, we need to make
// sure to finish it as we're making a new activity topmost.
- if (shouldSleepActivities() && mLastNoHistoryActivity != null &&
- !mLastNoHistoryActivity.finishing) {
+ if (shouldSleepActivities() && mLastNoHistoryActivity != null
+ && !mLastNoHistoryActivity.finishing
+ && mLastNoHistoryActivity != next) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"no-history finish of " + mLastNoHistoryActivity + " on new resume");
mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index c11624c..76bd6ce 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -151,6 +151,12 @@
*/
private boolean mRemoved;
+ /**
+ * Whether the task display area should ignore fixed-orientation request. If {@code true}, it
+ * can never specify orientation, but show the fixed-orientation apps in the letterbox;
+ * otherwise, it rotates based on the fixed-orientation request when it has the focus.
+ */
+ private boolean mIgnoreOrientationRequest;
/**
* The id of a leaf task that most recently being moved to front.
@@ -641,11 +647,30 @@
}
}
+ /**
+ * Sets whether the task display area should ignore fixed-orientation request from apps.
+ * @return Whether the display orientation changed
+ */
+ boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) {
+ if (mIgnoreOrientationRequest == ignoreOrientationRequest) {
+ return false;
+ }
+
+ mIgnoreOrientationRequest = ignoreOrientationRequest;
+ if (isLastFocused()) {
+ // Update orientation if this TDA is the last focused, otherwise it shouldn't affect
+ // the display.
+ return mDisplayContent.updateOrientation();
+ }
+
+ return false;
+ }
+
@Override
int getOrientation(int candidate) {
- // Only allow to specify orientation if this TDA has the focus.
- // TODO(b/155431879) Add option to never allow a TDA to specify orientation.
- if (!isLastFocused()) {
+ // Only allow to specify orientation if this TDA is not set to ignore orientation request,
+ // and it has the focus.
+ if (mIgnoreOrientationRequest || !isLastFocused()) {
return SCREEN_ORIENTATION_UNSET;
}
@@ -1571,10 +1596,12 @@
return topRunning;
}
+ // TODO (b/157876447): switch to Task related name
protected int getStackCount() {
return mChildren.size();
}
+ // TODO (b/157876447): switch to Task related name
protected Task getStackAt(int index) {
return mChildren.get(index);
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index abe6329..a6f0f46 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -96,7 +96,6 @@
boolean mDragEnded;
IBinder mClientCallback;
- InputChannel mServerChannel;
InputChannel mClientChannel;
InputApplicationHandle mDragApplicationHandle;
InputWindowHandle mDragWindowHandle;
@@ -220,10 +219,7 @@
}
mDisplayContent = displayContent;
- final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
- mServerChannel = channels[0];
- mClientChannel = channels[1];
- mService.mInputManager.registerInputChannel(mServerChannel);
+ mClientChannel = mService.mInputManager.createInputChannel(TAG);
mInputEventReceiver = new WindowPositionerEventReceiver(
mClientChannel, mService.mAnimationHandler.getLooper(),
@@ -237,7 +233,7 @@
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle,
displayContent.getDisplayId());
mDragWindowHandle.name = TAG;
- mDragWindowHandle.token = mServerChannel.getToken();
+ mDragWindowHandle.token = mClientChannel.getToken();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
@@ -299,14 +295,12 @@
}
mService.mTaskPositioningController.hideInputSurface(mDisplayContent.getDisplayId());
- mService.mInputManager.unregisterInputChannel(mServerChannel.getToken());
+ mService.mInputManager.removeInputChannel(mClientChannel.getToken());
mInputEventReceiver.dispose();
mInputEventReceiver = null;
mClientChannel.dispose();
- mServerChannel.dispose();
mClientChannel = null;
- mServerChannel = null;
mDragWindowHandle = null;
mDragApplicationHandle = null;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index b9a449f..9d35c25 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -188,7 +188,7 @@
transferFocusFromWin = displayContent.mCurrentFocus;
}
if (!mInputManager.transferTouchFocus(
- transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
+ transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
cleanUpTaskPositioner();
return false;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index ae8f7a5..2b93080 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -32,6 +32,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SYNC_ENGINE;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.AppTransition.MAX_APP_TRANSITION_DURATION;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -2886,6 +2887,8 @@
// If we are invisible, no need to sync, likewise if we are already engaged in a sync,
// we can't support overlapping syncs on a single container yet.
if (!isVisible() || mWaitingListener != null) {
+ ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "- NOT adding to sync: visible=%b "
+ + "hasListener=%b", isVisible(), mWaitingListener != null);
return false;
}
mUsingBLASTSyncTransaction = true;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8c2619d..d9c574c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1005,7 +1005,7 @@
final Configuration mTempConfiguration = new Configuration();
- final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
+ final HighRefreshRateDenylist mHighRefreshRateDenylist;
// If true, only the core apps and services are being launched because the device
// is in a special boot mode, such as being encrypted or waiting for a decryption password.
@@ -1302,7 +1302,7 @@
this, mInputManager, mActivityTaskManager, mH.getLooper());
mDragDropController = new DragDropController(this, mH.getLooper());
- mHighRefreshRateBlacklist = HighRefreshRateBlacklist.create(context.getResources());
+ mHighRefreshRateDenylist = HighRefreshRateDenylist.create(context.getResources());
mConstants = new WindowManagerConstants(this, DeviceConfigInterface.REAL);
mConstants.start(new HandlerExecutor(mH));
@@ -5939,7 +5939,7 @@
private void dumpHighRefreshRateBlacklist(PrintWriter pw) {
pw.println("WINDOW MANAGER HIGH REFRESH RATE BLACKLIST (dumpsys window refresh)");
- mHighRefreshRateBlacklist.dump(pw);
+ mHighRefreshRateDenylist.dump(pw);
}
private void dumpTraceStatus(PrintWriter pw) {
@@ -7997,8 +7997,7 @@
updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface,
name, applicationHandle, flags, privateFlags, type, null /* region */);
- clientChannel.transferTo(outInputChannel);
- clientChannel.dispose();
+ clientChannel.copyTo(outInputChannel);
}
private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c7cad2f..999181d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -306,6 +306,19 @@
return effects;
}
+ private int applyTaskDisplayAreaChanges(TaskDisplayArea taskDisplayArea,
+ WindowContainerTransaction.Change c) {
+ int effects = applyDisplayAreaChanges(taskDisplayArea, c);
+ if ((c.getChangeMask()
+ & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
+ if (taskDisplayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
+
+ return effects;
+ }
+
private int applyDisplayAreaChanges(WindowContainer container,
WindowContainerTransaction.Change c) {
final int[] effects = new int[1];
@@ -388,7 +401,9 @@
int effects = applyChanges(wc, c);
- if (wc instanceof DisplayArea) {
+ if (wc instanceof TaskDisplayArea) {
+ effects |= applyTaskDisplayAreaChanges((TaskDisplayArea) wc, c);
+ } else if (wc instanceof DisplayArea) {
effects |= applyDisplayAreaChanges(wc, c);
} else if (wc instanceof Task) {
effects |= applyTaskChanges(wc.asTask(), c);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 67d230a..268281b 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -41,7 +41,6 @@
import static com.android.server.wm.Task.ActivityState.STARTED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -195,13 +194,16 @@
// Last configuration that was reported to the process.
private final Configuration mLastReportedConfiguration = new Configuration();
- // Configuration that is waiting to be dispatched to the process.
- private Configuration mPendingConfiguration;
+ /** Whether the process configuration is waiting to be dispatched to the process. */
+ private boolean mHasPendingConfigurationChange;
// Registered display id as a listener to override config change
private int mDisplayId;
private ActivityRecord mConfigActivityRecord;
// Whether the activity config override is allowed for this process.
private volatile boolean mIsActivityConfigOverrideAllowed = true;
+ /** Non-zero to pause dispatching process configuration change. */
+ private int mPauseConfigurationDispatchCount;
+
/**
* Activities that hosts some UI drawn by the current process. The activities live
* in another process. This is used to check if the process is currently showing anything
@@ -465,8 +467,10 @@
* Allows background activity starts using token {@code entity}. Optionally, you can provide
* {@code originatingToken} if you have one such originating token, this is useful for tracing
* back the grant in the case of the notification token.
+ *
+ * If {@code entity} is already added, this method will update its {@code originatingToken}.
*/
- public void addAllowBackgroundActivityStartsToken(Binder entity,
+ public void addOrUpdateAllowBackgroundActivityStartsToken(Binder entity,
@Nullable IBinder originatingToken) {
synchronized (mAtm.mGlobalLock) {
mBackgroundActivityStartTokens.put(entity, originatingToken);
@@ -475,7 +479,7 @@
/**
* Removes token {@code entity} that allowed background activity starts added via {@link
- * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+ * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
*/
public void removeAllowBackgroundActivityStartsToken(Binder entity) {
synchronized (mAtm.mGlobalLock) {
@@ -485,7 +489,7 @@
/**
* Returns true if background activity starts are allowed by any token added via {@link
- * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+ * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
*/
public boolean areBackgroundActivityStartsAllowedByToken() {
synchronized (mAtm.mGlobalLock) {
@@ -1242,8 +1246,10 @@
onMergedOverrideConfigurationChanged(Configuration.EMPTY);
}
- private void registerActivityConfigurationListener(ActivityRecord activityRecord) {
- if (activityRecord == null || activityRecord.containsListener(this)) {
+ void registerActivityConfigurationListener(ActivityRecord activityRecord) {
+ if (activityRecord == null || activityRecord.containsListener(this)
+ // Check for the caller from outside of this class.
+ || !mIsActivityConfigOverrideAllowed) {
return;
}
// A process can only register to one activityRecord to listen to the override configuration
@@ -1295,25 +1301,25 @@
@Override
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
- super.onRequestedOverrideConfigurationChanged(
- sanitizeProcessConfiguration(overrideConfiguration));
+ super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
}
@Override
public void onMergedOverrideConfigurationChanged(Configuration mergedOverrideConfig) {
- super.onRequestedOverrideConfigurationChanged(
- sanitizeProcessConfiguration(mergedOverrideConfig));
+ super.onRequestedOverrideConfigurationChanged(mergedOverrideConfig);
}
- private static Configuration sanitizeProcessConfiguration(Configuration config) {
+ @Override
+ void resolveOverrideConfiguration(Configuration newParentConfig) {
+ super.resolveOverrideConfiguration(newParentConfig);
+ final Configuration resolvedConfig = getResolvedOverrideConfiguration();
// Make sure that we don't accidentally override the activity type.
- if (config.windowConfiguration.getActivityType() != ACTIVITY_TYPE_UNDEFINED) {
- final Configuration sanitizedConfig = new Configuration(config);
- sanitizedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
- return sanitizedConfig;
- }
-
- return config;
+ resolvedConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ // Activity has an independent ActivityRecord#mConfigurationSeq. If this process registers
+ // activity configuration, its config seq shouldn't go backwards by activity configuration.
+ // Otherwise if other places send wpc.getConfiguration() to client, the configuration may
+ // be ignored due to the seq is older.
+ resolvedConfig.seq = newParentConfig.seq;
}
private void updateConfiguration() {
@@ -1331,11 +1337,7 @@
if (mListener.isCached()) {
// This process is in a cached state. We will delay delivering the config change to the
// process until the process is no longer cached.
- if (mPendingConfiguration == null) {
- mPendingConfiguration = new Configuration(config);
- } else {
- mPendingConfiguration.setTo(config);
- }
+ mHasPendingConfigurationChange = true;
return;
}
@@ -1343,6 +1345,11 @@
}
private void dispatchConfigurationChange(Configuration config) {
+ if (mPauseConfigurationDispatchCount > 0) {
+ mHasPendingConfigurationChange = true;
+ return;
+ }
+ mHasPendingConfigurationChange = false;
if (mThread == null) {
if (Build.IS_DEBUGGABLE && mHasImeService) {
// TODO (b/135719017): Temporary log for debugging IME service.
@@ -1368,7 +1375,7 @@
}
}
- private void setLastReportedConfiguration(Configuration config) {
+ void setLastReportedConfiguration(Configuration config) {
mLastReportedConfiguration.setTo(config);
}
@@ -1376,6 +1383,30 @@
return mLastReportedConfiguration;
}
+ void pauseConfigurationDispatch() {
+ mPauseConfigurationDispatchCount++;
+ }
+
+ void resumeConfigurationDispatch() {
+ mPauseConfigurationDispatchCount--;
+ }
+
+ /**
+ * This is called for sending {@link android.app.servertransaction.LaunchActivityItem}.
+ * The caller must call {@link #setLastReportedConfiguration} if the delivered configuration
+ * is newer.
+ */
+ Configuration prepareConfigurationForLaunchingActivity() {
+ final Configuration config = getConfiguration();
+ if (mHasPendingConfigurationChange) {
+ mHasPendingConfigurationChange = false;
+ // The global configuration may not change, so the client process may have the same
+ // config seq. This increment ensures that the client won't ignore the configuration.
+ config.seq = mAtm.increaseConfigurationSeqLocked();
+ }
+ return config;
+ }
+
/** Returns the total time (in milliseconds) spent executing in both user and system code. */
public long getCpuTime() {
return (mListener != null) ? mListener.getCpuTime() : 0;
@@ -1467,10 +1498,8 @@
public void onProcCachedStateChanged(boolean isCached) {
if (!isCached) {
synchronized (mAtm.mGlobalLockWithoutBoost) {
- if (mPendingConfiguration != null) {
- final Configuration config = mPendingConfiguration;
- mPendingConfiguration = null;
- dispatchConfigurationChange(config);
+ if (mHasPendingConfigurationChange) {
+ dispatchConfigurationChange(getConfiguration());
}
}
}
@@ -1575,10 +1604,14 @@
pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
}
if (mBackgroundActivityStartTokens.size() > 0) {
- pw.print(prefix); pw.println("Background activity start tokens:");
+ pw.print(prefix);
+ pw.println("Background activity start tokens (token: originating token):");
for (int i = 0; i < mBackgroundActivityStartTokens.size(); i++) {
pw.print(prefix); pw.print(" - ");
- pw.println(mBackgroundActivityStartTokens.keyAt(i));
+ pw.print(mBackgroundActivityStartTokens.keyAt(i));
+ pw.print(": ");
+ pw.println(mBackgroundActivityStartTokens.valueAt(i));
+
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e4a079d..1f7457c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -556,7 +556,6 @@
// Input channel and input window handle used by the input dispatcher.
final InputWindowHandle mInputWindowHandle;
InputChannel mInputChannel;
- private InputChannel mClientChannel;
// Used to improve performance of toString()
private String mStringNameCache;
@@ -2478,20 +2477,15 @@
throw new IllegalStateException("Window already has an input channel.");
}
String name = getName();
- InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
- mInputChannel = inputChannels[0];
- mClientChannel = inputChannels[1];
- mWmService.mInputManager.registerInputChannel(mInputChannel);
+ mInputChannel = mWmService.mInputManager.createInputChannel(name);
mInputWindowHandle.token = mInputChannel.getToken();
if (outInputChannel != null) {
- mClientChannel.transferTo(outInputChannel);
- mClientChannel.dispose();
- mClientChannel = null;
+ mInputChannel.copyTo(outInputChannel);
} else {
// If the window died visible, we setup a fake input channel, so that taps
// can still detected by input monitor channel, and we can relaunch the app.
// Create fake event receiver that simply reports all events as handled.
- mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
+ mDeadWindowEventReceiver = new DeadWindowEventReceiver(mInputChannel);
}
mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
@@ -2504,15 +2498,11 @@
// unregister server channel first otherwise it complains about broken channel
if (mInputChannel != null) {
- mWmService.mInputManager.unregisterInputChannel(mInputChannel.getToken());
+ mWmService.mInputManager.removeInputChannel(mInputChannel.getToken());
mInputChannel.dispose();
mInputChannel = null;
}
- if (mClientChannel != null) {
- mClientChannel.dispose();
- mClientChannel = null;
- }
mWmService.mKeyInterceptionInfoForToken.remove(mInputWindowHandle.token);
mWmService.mInputToWindowMap.remove(mInputWindowHandle.token);
mInputWindowHandle.token = null;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 5e81400..029c158 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -441,10 +441,6 @@
return mSurfaceController;
}
- if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
- windowType = SurfaceControl.WINDOW_TYPE_DONT_SCREENSHOT;
- }
-
w.setHasSurface(false);
if (DEBUG_ANIM) {
@@ -462,6 +458,10 @@
flags |= SurfaceControl.SECURE;
}
+ if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
+ flags |= SurfaceControl.SKIP_SCREENSHOT;
+ }
+
calculateSurfaceBounds(w, attrs, mTmpSize);
final int width = mTmpSize.width();
final int height = mTmpSize.height();
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index bc4d9a9..fa0b8cc 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -181,6 +181,12 @@
}
}
}
+
+ /** The state may not only be used by self. Make sure to leave the influence by others. */
+ void disassociate(WindowToken token) {
+ mAssociatedTokens.remove(token);
+ mRotatedContainers.remove(token);
+ }
}
private class DeathRecipient implements IBinder.DeathRecipient {
@@ -548,7 +554,7 @@
void applyFixedRotationTransform(DisplayInfo info, DisplayFrames displayFrames,
Configuration config) {
if (mFixedRotationTransformState != null) {
- cleanUpFixedRotationTransformState(true /* replacing */);
+ mFixedRotationTransformState.disassociate(this);
}
mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
new Configuration(config), mDisplayContent.getRotation());
@@ -556,8 +562,7 @@
mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames,
mFixedRotationTransformState.mInsetsState,
mFixedRotationTransformState.mBarContentFrames);
- onConfigurationChanged(getParent().getConfiguration());
- notifyFixedRotationTransform(true /* enabled */);
+ onFixedRotationStatePrepared();
}
/**
@@ -570,12 +575,29 @@
return;
}
if (mFixedRotationTransformState != null) {
- cleanUpFixedRotationTransformState(true /* replacing */);
+ mFixedRotationTransformState.disassociate(this);
}
mFixedRotationTransformState = fixedRotationState;
fixedRotationState.mAssociatedTokens.add(this);
- onConfigurationChanged(getParent().getConfiguration());
+ onFixedRotationStatePrepared();
+ }
+
+ /**
+ * Makes the rotated states take effect for this window container and its client process.
+ * This should only be called when {@link #mFixedRotationTransformState} is non-null.
+ */
+ private void onFixedRotationStatePrepared() {
+ // Send the adjustment info first so when the client receives configuration change, it can
+ // get the rotated display metrics.
notifyFixedRotationTransform(true /* enabled */);
+ // Resolve the rotated configuration.
+ onConfigurationChanged(getParent().getConfiguration());
+ final ActivityRecord r = asActivityRecord();
+ if (r != null && r.hasProcess()) {
+ // The application needs to be configured as in a rotated environment for compatibility.
+ // This registration will send the rotated configuration to its process.
+ r.app.registerActivityConfigurationListener(r);
+ }
}
/**
@@ -626,21 +648,12 @@
// The state is cleared at the end, because it is used to indicate that other windows can
// use seamless rotation when applying rotation to display.
for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
- state.mAssociatedTokens.get(i).cleanUpFixedRotationTransformState(
- false /* replacing */);
+ final WindowToken token = state.mAssociatedTokens.get(i);
+ token.mFixedRotationTransformState = null;
+ token.notifyFixedRotationTransform(false /* enabled */);
}
}
- private void cleanUpFixedRotationTransformState(boolean replacing) {
- if (replacing && mFixedRotationTransformState.mAssociatedTokens.size() > 1) {
- // The state is not only used by self. Make sure to leave the influence by others.
- mFixedRotationTransformState.mAssociatedTokens.remove(this);
- mFixedRotationTransformState.mRotatedContainers.remove(this);
- }
- mFixedRotationTransformState = null;
- notifyFixedRotationTransform(false /* enabled */);
- }
-
/** Notifies application side to enable or disable the rotation adjustment of display info. */
private void notifyFixedRotationTransform(boolean enabled) {
FixedRotationAdjustments adjustments = null;
@@ -704,8 +717,9 @@
if (!isFixedRotationTransforming()) {
return null;
}
- return new FixedRotationAdjustments(mFixedRotationTransformState.mDisplayInfo.rotation,
- mFixedRotationTransformState.mDisplayInfo.displayCutout);
+ final DisplayInfo displayInfo = mFixedRotationTransformState.mDisplayInfo;
+ return new FixedRotationAdjustments(displayInfo.rotation, displayInfo.appWidth,
+ displayInfo.appHeight, displayInfo.displayCutout);
}
@Override
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 1b649fd..44462c3 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -54,6 +54,7 @@
"com_android_server_UsbDescriptorParser.cpp",
"com_android_server_UsbMidiDevice.cpp",
"com_android_server_UsbHostManager.cpp",
+ "com_android_server_VibratorManagerService.cpp",
"com_android_server_VibratorService.cpp",
"com_android_server_PersistentDataBlockService.cpp",
"com_android_server_am_CachedAppOptimizer.cpp",
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 8646a53..4c017f5 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -2,6 +2,7 @@
per-file com_android_server_lights_LightsService.cpp = michaelwr@google.com, santoscordon@google.com
# Haptics
+per-file com_android_server_VibratorManagerService.cpp = michaelwr@google.com
per-file com_android_server_VibratorService.cpp = michaelwr@google.com
# Input
diff --git a/services/core/jni/com_android_server_VibratorManagerService.cpp b/services/core/jni/com_android_server_VibratorManagerService.cpp
new file mode 100644
index 0000000..dae9cef
--- /dev/null
+++ b/services/core/jni/com_android_server_VibratorManagerService.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "VibratorManagerService"
+
+#include <nativehelper/JNIHelp.h>
+#include "android_runtime/AndroidRuntime.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+#include <utils/Log.h>
+#include <utils/misc.h>
+
+#include <vibratorservice/VibratorManagerHalWrapper.h>
+
+namespace android {
+
+class NativeVibratorManagerService {
+public:
+ NativeVibratorManagerService() : mHal(std::make_unique<vibrator::LegacyManagerHalWrapper>()) {}
+ ~NativeVibratorManagerService() = default;
+
+ vibrator::ManagerHalWrapper* hal() const { return mHal.get(); }
+
+private:
+ const std::unique_ptr<vibrator::ManagerHalWrapper> mHal;
+};
+
+static void destroyNativeService(void* ptr) {
+ NativeVibratorManagerService* service = reinterpret_cast<NativeVibratorManagerService*>(ptr);
+ if (service) {
+ delete service;
+ }
+}
+
+static jlong nativeInit(JNIEnv* /* env */, jclass /* clazz */) {
+ std::unique_ptr<NativeVibratorManagerService> service =
+ std::make_unique<NativeVibratorManagerService>();
+ return reinterpret_cast<jlong>(service.release());
+}
+
+static jlong nativeGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
+}
+
+static jintArray nativeGetVibratorIds(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+ NativeVibratorManagerService* service =
+ reinterpret_cast<NativeVibratorManagerService*>(servicePtr);
+ if (service == nullptr) {
+ ALOGE("nativeGetVibratorIds failed because native service was not initialized");
+ return nullptr;
+ }
+ auto result = service->hal()->getVibratorIds();
+ if (!result.isOk()) {
+ return nullptr;
+ }
+ std::vector<int32_t> vibratorIds = result.value();
+ jintArray ids = env->NewIntArray(vibratorIds.size());
+ env->SetIntArrayRegion(ids, 0, vibratorIds.size(), reinterpret_cast<jint*>(vibratorIds.data()));
+ return ids;
+}
+
+static const JNINativeMethod method_table[] = {
+ {"nativeInit", "()J", (void*)nativeInit},
+ {"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer},
+ {"nativeGetVibratorIds", "(J)[I", (void*)nativeGetVibratorIds},
+};
+
+int register_android_server_VibratorManagerService(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/server/VibratorManagerService", method_table,
+ NELEM(method_table));
+}
+
+}; // namespace android
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 46136ca..17a05f3 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -206,10 +206,12 @@
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- status_t registerInputChannel(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel);
- status_t registerInputMonitor(JNIEnv* env, const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor);
- status_t unregisterInputChannel(JNIEnv* env, const sp<IBinder>& connectionToken);
+ base::Result<std::unique_ptr<InputChannel>> createInputChannel(JNIEnv* env,
+ const std::string& name);
+ base::Result<std::unique_ptr<InputChannel>> createInputMonitor(JNIEnv* env, int32_t displayId,
+ bool isGestureMonitor,
+ const std::string& name);
+ status_t removeInputChannel(JNIEnv* env, const sp<IBinder>& connectionToken);
status_t pilferPointers(const sp<IBinder>& token);
void displayRemoved(JNIEnv* env, int32_t displayId);
@@ -432,24 +434,22 @@
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
-status_t NativeInputManager::registerInputChannel(
- JNIEnv* /* env */, const std::shared_ptr<InputChannel>& inputChannel) {
+base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
+ JNIEnv* /* env */, const std::string& name) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
+ return mInputManager->getDispatcher()->createInputChannel(name);
}
-status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
- const std::shared_ptr<InputChannel>& inputChannel,
- int32_t displayId, bool isGestureMonitor) {
+base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputMonitor(
+ JNIEnv* /* env */, int32_t displayId, bool isGestureMonitor, const std::string& name) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->registerInputMonitor(
- inputChannel, displayId, isGestureMonitor);
+ return mInputManager->getDispatcher()->createInputMonitor(displayId, isGestureMonitor, name);
}
-status_t NativeInputManager::unregisterInputChannel(JNIEnv* /* env */,
- const sp<IBinder>& connectionToken) {
+status_t NativeInputManager::removeInputChannel(JNIEnv* /* env */,
+ const sp<IBinder>& connectionToken) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->unregisterInputChannel(connectionToken);
+ return mInputManager->getDispatcher()->removeInputChannel(connectionToken);
}
status_t NativeInputManager::pilferPointers(const sp<IBinder>& token) {
@@ -1352,80 +1352,83 @@
return result;
}
-static void throwInputChannelNotInitialized(JNIEnv* env) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "inputChannel is not initialized");
-}
-
static void handleInputChannelDisposed(JNIEnv* env, jobject /* inputChannelObj */,
const std::shared_ptr<InputChannel>& inputChannel,
void* data) {
NativeInputManager* im = static_cast<NativeInputManager*>(data);
- ALOGW("Input channel object '%s' was disposed without first being unregistered with "
- "the input manager!", inputChannel->getName().c_str());
- im->unregisterInputChannel(env, inputChannel->getConnectionToken());
+ ALOGW("Input channel object '%s' was disposed without first being removed with "
+ "the input manager!",
+ inputChannel->getName().c_str());
+ im->removeInputChannel(env, inputChannel->getConnectionToken());
}
-static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject inputChannelObj) {
+static jobject nativeCreateInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
+ jstring nameObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- std::shared_ptr<InputChannel> inputChannel =
- android_view_InputChannel_getInputChannel(env, inputChannelObj);
- if (inputChannel == nullptr) {
- throwInputChannelNotInitialized(env);
- return;
+ ScopedUtfChars nameChars(env, nameObj);
+ std::string name = nameChars.c_str();
+
+ base::Result<std::unique_ptr<InputChannel>> inputChannel = im->createInputChannel(env, name);
+
+ if (!inputChannel) {
+ std::string message = inputChannel.error().message();
+ message += StringPrintf(" Status=%d", inputChannel.error().code());
+ jniThrowRuntimeException(env, message.c_str());
+ return nullptr;
}
- status_t status = im->registerInputChannel(env, inputChannel);
-
- if (status) {
- std::string message;
- message += StringPrintf("Failed to register input channel. status=%d", status);
- jniThrowRuntimeException(env, message.c_str());
- return;
+ jobject inputChannelObj =
+ android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
+ if (!inputChannelObj) {
+ return nullptr;
}
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
+ return inputChannelObj;
}
-static void nativeRegisterInputMonitor(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject inputChannelObj, jint displayId, jboolean isGestureMonitor) {
+static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId,
+ jboolean isGestureMonitor, jstring nameObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- std::shared_ptr<InputChannel> inputChannel =
- android_view_InputChannel_getInputChannel(env, inputChannelObj);
- if (inputChannel == nullptr) {
- throwInputChannelNotInitialized(env);
- return;
- }
-
if (displayId == ADISPLAY_ID_NONE) {
std::string message = "InputChannel used as a monitor must be associated with a display";
jniThrowRuntimeException(env, message.c_str());
- return;
+ return nullptr;
}
- status_t status = im->registerInputMonitor(env, inputChannel, displayId, isGestureMonitor);
+ ScopedUtfChars nameChars(env, nameObj);
+ std::string name = nameChars.c_str();
- if (status) {
- std::string message = StringPrintf("Failed to register input channel. status=%d", status);
+ base::Result<std::unique_ptr<InputChannel>> inputChannel =
+ im->createInputMonitor(env, displayId, isGestureMonitor, name);
+
+ if (!inputChannel) {
+ std::string message = inputChannel.error().message();
+ message += StringPrintf(" Status=%d", inputChannel.error().code());
jniThrowRuntimeException(env, message.c_str());
- return;
+ return nullptr;
}
+
+ jobject inputChannelObj =
+ android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));
+ if (!inputChannelObj) {
+ return nullptr;
+ }
+ return inputChannelObj;
}
-static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr,
- jobject tokenObj) {
+static void nativeRemoveInputChannel(JNIEnv* env, jclass /* clazz */, jlong ptr, jobject tokenObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<IBinder> token = ibinderForJavaObject(env, tokenObj);
- status_t status = im->unregisterInputChannel(env, token);
- if (status && status != BAD_VALUE) { // ignore already unregistered channel
+ status_t status = im->removeInputChannel(env, token);
+ if (status && status != BAD_VALUE) { // ignore already removed channel
std::string message;
- message += StringPrintf("Failed to unregister input channel. status=%d", status);
+ message += StringPrintf("Failed to remove input channel. status=%d", status);
jniThrowRuntimeException(env, message.c_str());
}
}
@@ -1780,12 +1783,11 @@
{"nativeGetKeyCodeState", "(JIII)I", (void*)nativeGetKeyCodeState},
{"nativeGetSwitchState", "(JIII)I", (void*)nativeGetSwitchState},
{"nativeHasKeys", "(JII[I[Z)Z", (void*)nativeHasKeys},
- {"nativeRegisterInputChannel", "(JLandroid/view/InputChannel;)V",
- (void*)nativeRegisterInputChannel},
- {"nativeRegisterInputMonitor", "(JLandroid/view/InputChannel;IZ)V",
- (void*)nativeRegisterInputMonitor},
- {"nativeUnregisterInputChannel", "(JLandroid/os/IBinder;)V",
- (void*)nativeUnregisterInputChannel},
+ {"nativeCreateInputChannel", "(JLjava/lang/String;)Landroid/view/InputChannel;",
+ (void*)nativeCreateInputChannel},
+ {"nativeCreateInputMonitor", "(JIZLjava/lang/String;)Landroid/view/InputChannel;",
+ (void*)nativeCreateInputMonitor},
+ {"nativeRemoveInputChannel", "(JLandroid/os/IBinder;)V", (void*)nativeRemoveInputChannel},
{"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers},
{"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled},
{"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode},
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index e7f6db9..48d5244 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -38,6 +38,7 @@
int register_android_server_UsbMidiDevice(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_vr_VrManagerService(JNIEnv* env);
+int register_android_server_VibratorManagerService(JNIEnv* env);
int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
@@ -92,6 +93,7 @@
register_android_server_UsbAlsaJackDetector(env);
register_android_server_UsbHostManager(env);
register_android_server_vr_VrManagerService(env);
+ register_android_server_VibratorManagerService(env);
register_android_server_VibratorService(vm, env);
register_android_server_SystemServer(env);
register_android_server_location_GnssLocationProvider(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
index 5193fa8..b6b4d8a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
@@ -58,4 +58,12 @@
@Nullable public ComponentName getComponentName() {
return mComponentName;
}
+
+ public boolean hasAdminComponent() {
+ return mComponentName != null;
+ }
+
+ public boolean hasPackage() {
+ return mPackageName != null;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 183a149..8f1fb12 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1567,6 +1567,54 @@
}
/**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
+ * component name is provided, look up the component name and fill it in for the caller.
+ */
+ private CallerIdentity getCallerIdentityOptionalAdmin(@Nullable ComponentName adminComponent) {
+ if (adminComponent == null) {
+ ActiveAdmin admin = getActiveAdminOfCaller();
+ if (admin != null) {
+ return getCallerIdentity(admin.info.getComponent());
+ }
+ throw new SecurityException("Caller is not an active admin");
+ } else {
+ return getCallerIdentity(adminComponent);
+ }
+ }
+
+ /**
+ * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no
+ * package name is provided, look up the package name and fill it in for the caller.
+ */
+ private CallerIdentity getCallerIdentityOptionalPackage(@Nullable String callerPackage) {
+ if (callerPackage == null) {
+ ActiveAdmin admin = getActiveAdminOfCaller();
+ if (admin != null) {
+ return getCallerIdentity(admin.info.getPackageName());
+ }
+ throw new SecurityException("Caller is not an active admin");
+ } else {
+ return getCallerIdentity(callerPackage);
+ }
+ }
+
+ /**
+ * Retrieves the active admin of the caller. This method should not be called directly and
+ * should only be called by {@link #getCallerIdentityOptionalAdmin} or
+ * {@link #getCallerIdentityOptionalPackage}.
+ */
+ private ActiveAdmin getActiveAdminOfCaller() {
+ final int callerUid = mInjector.binderGetCallingUid();
+ final DevicePolicyData policy = getUserData(UserHandle.getUserId(callerUid));
+ for (ActiveAdmin admin : policy.mAdminList) {
+ if (admin.getUid() == callerUid) {
+ return admin;
+ }
+ }
+ return null;
+ }
+
+ /**
* Checks if the device is in COMP mode, and if so migrates it to managed profile on a
* corporate owned device.
*/
@@ -3071,7 +3119,7 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = getCallerIdentity(adminReceiver);
+ final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -3630,6 +3678,8 @@
@Override
public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+ Objects.requireNonNull(admin, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
List<String> changedProviders = null;
@@ -3663,6 +3713,8 @@
@Override
public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) {
+ Objects.requireNonNull(admin, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
List<String> changedProviders = null;
@@ -3696,6 +3748,8 @@
@Override
public List<String> getCrossProfileWidgetProviders(ComponentName admin) {
+ Objects.requireNonNull(admin, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(admin);
Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
@@ -4833,8 +4887,9 @@
byte[] cert, byte[] chain, String alias, boolean requestAccess,
boolean isUserSelectable) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
final long id = mInjector.binderClearCallingIdentity();
try {
@@ -4872,8 +4927,9 @@
@Override
public boolean removeKeyPair(ComponentName who, String callerPackage, String alias) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
final long id = Binder.clearCallingIdentity();
try {
@@ -4908,8 +4964,9 @@
Preconditions.checkStringNotEmpty(packageName, "Package to grant to cannot be empty");
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
final int granteeUid;
try {
@@ -5052,8 +5109,9 @@
enforceCallerCanRequestDeviceIdAttestation(caller);
enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags);
} else {
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
}
// As the caller will be granted access to the key, ensure no UID was specified, as
@@ -5147,8 +5205,9 @@
public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias,
byte[] cert, byte[] chain, boolean isUserSelectable) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)));
final long id = mInjector.binderClearCallingIdentity();
try (final KeyChainConnection keyChainConnection =
@@ -5613,6 +5672,8 @@
public boolean setAlwaysOnVpnPackage(ComponentName who, String vpnPackage, boolean lockdown,
List<String> lockdownWhitelist)
throws SecurityException {
+ Objects.requireNonNull(who, "ComponentName is null");
+
enforceProfileOrDeviceOwner(who);
final CallerIdentity caller = getCallerIdentity(who);
@@ -5752,16 +5813,16 @@
}
final CallerIdentity caller = getCallerIdentity();
boolean calledByProfileOwnerOnOrgOwnedDevice =
- isProfileOwnerOfOrganizationOwnedDevice(caller);
+ isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId());
if (calledOnParentInstance) {
Preconditions.checkCallAuthorization(calledByProfileOwnerOnOrgOwnedDevice,
"Wiping the entire device can only be done by a profile owner on "
+ "organization-owned device.");
}
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
- Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || calledByProfileOwnerOnOrgOwnedDevice,
- "Only device owners or proflie owners of organization-owned device can set "
+ Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid())
+ || calledByProfileOwnerOnOrgOwnedDevice,
+ "Only device owners or profile owners of organization-owned device can set "
+ "WIPE_RESET_PROTECTION_DATA");
}
@@ -5960,9 +6021,7 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = comp != null
- ? getCallerIdentity(comp)
- : getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentityOptionalAdmin(comp);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
@@ -6399,9 +6458,7 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = who != null
- ? getCallerIdentity(who)
- : getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentityOptionalAdmin(who);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -6435,9 +6492,7 @@
}
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = callerPackage != null
- ? getCallerIdentity(callerPackage)
- : getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentityOptionalPackage(callerPackage);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
// It's not critical here, but let's make sure the package name is correct, in case
@@ -6546,9 +6601,8 @@
return false;
}
if (parent) {
- Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity(who)));
+ isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
}
synchronized (getLockObject()) {
@@ -6934,9 +6988,8 @@
return false;
}
if (parent) {
- Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(
- isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity(who)));
+ isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId()));
}
synchronized (getLockObject()) {
@@ -7070,8 +7123,9 @@
}
Objects.requireNonNull(packageList, "packageList is null");
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage()
+ && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
synchronized (getLockObject()) {
// Get the device owner
@@ -7097,8 +7151,9 @@
return null;
}
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage()
+ && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
// TODO In split system user mode, allow apps on user 0 to query the list
synchronized (getLockObject()) {
@@ -8517,8 +8572,9 @@
public void setApplicationRestrictions(ComponentName who, String callerPackage,
String packageName, Bundle settings) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
mInjector.binderWithCleanCallingIdentity(() -> {
mUserManager.setApplicationRestrictions(packageName, settings,
@@ -8558,9 +8614,7 @@
Objects.requireNonNull(agent, "agent null");
Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
- final CallerIdentity caller = admin != null
- ? getCallerIdentity(admin)
- : getCallerIdentity();
+ final CallerIdentity caller = getCallerIdentityOptionalAdmin(admin);
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
synchronized (getLockObject()) {
@@ -9442,8 +9496,9 @@
public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
return mInjector.binderWithCleanCallingIdentity(() -> {
Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
@@ -9458,8 +9513,9 @@
public String[] setPackagesSuspended(ComponentName who, String callerPackage,
String[] packageNames, boolean suspended) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
String[] result = null;
synchronized (getLockObject()) {
@@ -9489,8 +9545,9 @@
@Override
public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
synchronized (getLockObject()) {
long id = mInjector.binderClearCallingIdentity();
@@ -9650,8 +9707,9 @@
public boolean setApplicationHidden(ComponentName who, String callerPackage, String packageName,
boolean hidden, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
boolean result;
@@ -9682,8 +9740,9 @@
public boolean isApplicationHidden(ComponentName who, String callerPackage,
String packageName, boolean parent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId();
synchronized (getLockObject()) {
@@ -9716,8 +9775,9 @@
@Override
public void enableSystemApp(ComponentName who, String callerPackage, String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)));
synchronized (getLockObject()) {
final boolean isDemo = isCurrentUserDemo();
@@ -9759,8 +9819,9 @@
@Override
public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP)));
int numberOfAppsInstalled = 0;
synchronized (getLockObject()) {
@@ -9827,8 +9888,10 @@
public boolean installExistingPackage(ComponentName who, String callerPackage,
String packageName) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage()
+ && isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE)));
boolean result;
synchronized (getLockObject()) {
@@ -9941,8 +10004,9 @@
public void setUninstallBlocked(ComponentName who, String callerPackage, String packageName,
boolean uninstallBlocked) {
final CallerIdentity caller = getCallerIdentity(who, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL)));
final int userId = caller.getUserId();
synchronized (getLockObject()) {
@@ -10489,6 +10553,8 @@
@Override
public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
@@ -11596,8 +11662,9 @@
@Override
public void setPermissionPolicy(ComponentName admin, String callerPackage, int policy) {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
DevicePolicyData userPolicy = getUserData(caller.getUserId());
@@ -11630,8 +11697,9 @@
Objects.requireNonNull(callback);
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
long ident = mInjector.binderClearCallingIdentity();
@@ -11694,9 +11762,9 @@
public int getPermissionGrantState(ComponentName admin, String callerPackage,
String packageName, String permission) throws RemoteException {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
- Preconditions.checkCallAuthorization(
- isSystemUid(caller) || isDeviceOwner(caller) || isProfileOwner(caller)
- || isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT));
+ Preconditions.checkCallAuthorization(isSystemUid(caller) || (caller.hasAdminComponent()
+ && (isProfileOwner(caller) || isDeviceOwner(caller)))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -13149,8 +13217,8 @@
return;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
synchronized (getLockObject()) {
if (enabled == isNetworkLoggingEnabledInternalLocked()) {
@@ -13267,9 +13335,9 @@
return false;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization(
- isDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)
- || hasCallingOrSelfPermission(permission.MANAGE_USERS));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))
+ || hasCallingOrSelfPermission(permission.MANAGE_USERS));
synchronized (getLockObject()) {
return isNetworkLoggingEnabledInternalLocked();
@@ -13295,8 +13363,8 @@
return null;
}
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- Preconditions.checkCallAuthorization(isDeviceOwner(caller)
- || isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING));
+ Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller))
+ || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)));
Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked());
synchronized (getLockObject()) {
@@ -14494,6 +14562,8 @@
@Override
public List<String> getUserControlDisabledPackages(ComponentName who) {
+ Objects.requireNonNull(who, "ComponentName is null");
+
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDeviceOwner(caller));
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 44a07a1..e8bf468 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -308,6 +308,7 @@
}
mJobCondition.notify_all();
mJobProcessor.join();
+ mLooper->wake();
mCmdLooperThread.join();
mTimedQueue->stop();
mProgressUpdateJobQueue->stop();
@@ -1386,7 +1387,7 @@
}
void IncrementalService::runCmdLooper() {
- constexpr auto kTimeoutMsecs = 1000;
+ constexpr auto kTimeoutMsecs = -1;
while (mRunning.load(std::memory_order_relaxed)) {
mLooper->pollAll(kTimeoutMsecs);
}
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
index 1fa1b8f..75696da 100644
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
+++ b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
@@ -20,16 +20,20 @@
import org.junit.Test;
+import java.time.Duration;
+
public class LocationTimeZoneProviderRequestTest {
@Test
public void testParcelable() {
LocationTimeZoneProviderRequest.Builder builder =
new LocationTimeZoneProviderRequest.Builder()
- .setReportLocationTimeZone(true);
+ .setReportLocationTimeZone(false);
assertRoundTripParcelable(builder.build());
- builder.setReportLocationTimeZone(false);
+ builder.setReportLocationTimeZone(true)
+ .setInitializationTimeoutMillis(Duration.ofMinutes(5).toMillis());
+
assertRoundTripParcelable(builder.build());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
new file mode 100644
index 0000000..8a22a2f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/VibratorManagerServiceTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Tests for {@link VibratorManagerService}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:VibratorManagerServiceTest
+ */
+@Presubmit
+public class VibratorManagerServiceTest {
+
+ @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+ @Mock private VibratorManagerService.NativeWrapper mNativeWrapperMock;
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ private VibratorManagerService createService() {
+ return new VibratorManagerService(InstrumentationRegistry.getContext(),
+ new VibratorManagerService.Injector() {
+ @Override
+ VibratorManagerService.NativeWrapper getNativeWrapper() {
+ return mNativeWrapperMock;
+ }
+ });
+ }
+
+ @Test
+ public void createService_initializesNativeService() {
+ createService();
+ verify(mNativeWrapperMock).init();
+ }
+
+ @Test
+ public void getVibratorIds_withNullResultFromNative_returnsEmptyArray() {
+ when(mNativeWrapperMock.getVibratorIds()).thenReturn(null);
+ assertArrayEquals(new int[0], createService().getVibratorIds());
+ }
+
+ @Test
+ public void getVibratorIds_withNonEmptyResultFromNative_returnsSameArray() {
+ when(mNativeWrapperMock.getVibratorIds()).thenReturn(new int[]{ 1, 2 });
+ assertArrayEquals(new int[]{ 1, 2 }, createService().getVibratorIds());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
new file mode 100644
index 0000000..488e5cd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/BatteryStatsServiceTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.BatteryStatsImpl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(AndroidJUnit4.class)
+public final class BatteryStatsServiceTest {
+
+ private BatteryStatsService mBatteryStatsService;
+ private HandlerThread mBgThread;
+
+ @Before
+ public void setUp() {
+ final Context context = InstrumentationRegistry.getContext();
+ mBgThread = new HandlerThread("bg thread");
+ mBgThread.start();
+ mBatteryStatsService = new BatteryStatsService(context,
+ context.getCacheDir(), new Handler(mBgThread.getLooper()));
+ }
+
+ @After
+ public void tearDown() {
+ mBatteryStatsService.shutdown();
+ mBgThread.quitSafely();
+ }
+
+ @Test
+ public void testAwaitCompletion() throws Exception {
+ final CountDownLatch readyLatch = new CountDownLatch(2);
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ final CountDownLatch testLatch = new CountDownLatch(1);
+ final AtomicBoolean quiting = new AtomicBoolean(false);
+ final AtomicBoolean finished = new AtomicBoolean(false);
+ final int uid = Process.myUid();
+ final Thread noteThread = new Thread(() -> {
+ final int maxIterations = 1000;
+ final int eventCode = 12345;
+ final String eventName = "placeholder";
+ final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+
+ readyLatch.countDown();
+ try {
+ startLatch.await();
+ } catch (InterruptedException e) {
+ }
+
+ for (int i = 0; i < maxIterations && !quiting.get(); i++) {
+ synchronized (stats) {
+ mBatteryStatsService.noteEvent(eventCode, eventName, uid);
+ }
+ }
+ finished.set(true);
+ });
+ final Thread waitThread = new Thread(() -> {
+ readyLatch.countDown();
+ try {
+ startLatch.await();
+ } catch (InterruptedException e) {
+ }
+
+ do {
+ mBatteryStatsService.takeUidSnapshot(uid);
+ } while (!finished.get() && !quiting.get());
+
+ if (!quiting.get()) {
+ // do one more to ensure we've cleared the queue
+ mBatteryStatsService.takeUidSnapshot(uid);
+ }
+
+ testLatch.countDown();
+ });
+ noteThread.start();
+ waitThread.start();
+ readyLatch.await();
+ startLatch.countDown();
+
+ try {
+ assertTrue("Timed out in waiting for the completion of battery event handling",
+ testLatch.await(10 * 1000, TimeUnit.MILLISECONDS));
+ } finally {
+ quiting.set(true);
+ noteThread.interrupt();
+ noteThread.join(1000);
+ waitThread.interrupt();
+ waitThread.join(1000);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index cc1fdab..e011c797 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -123,8 +123,7 @@
final String[] config = {
"0:2:15", // ID0:Fingerprint:Strong
- "1:4:255", // ID1:Iris:Weak
- "2:8:4095", // ID2:Face:Convenience
+ "1:8:4095", // ID2:Face:Convenience
};
when(mInjector.getConfiguration(any())).thenReturn(config);
@@ -133,12 +132,14 @@
mAuthService.onStart();
final int fingerprintId = 0;
- final int irisId = 1;
- final int faceId = 2;
+ final int faceId = 1;
- verify(mFingerprintService).initializeConfiguration(eq(fingerprintId));
- verify(mIrisService).initializeConfiguration(eq(irisId));
- verify(mFaceService).initializeConfiguration(eq(faceId));
+ final int fingerprintStrength = 15;
+ final int faceStrength = 4095;
+
+ verify(mFingerprintService).initializeConfiguration(eq(fingerprintId),
+ eq(fingerprintStrength));
+ verify(mFaceService).initializeConfiguration(eq(faceId), eq(faceStrength));
}
diff --git a/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
index 7b88a0e..4f0cb32 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
@@ -32,9 +32,11 @@
public void setAndGetMatrix() {
final GlobalSaturationTintController tintController = new GlobalSaturationTintController();
tintController.setMatrix(50);
- assertThat(tintController.getMatrix()).hasValuesWithin(0.00001f)
- .of(new float[]{0.6155f, 0.1155f, 0.1155f, 0.0f, 0.3575f, 0.85749996f, 0.3575f,
- 0.0f, 0.036f, 0.036f, 0.536f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f});
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(
+ 0.6155f, 0.1155f, 0.1155f, 0.0f, 0.3575f, 0.85749996f, 0.3575f,
+ 0.0f, 0.036f, 0.036f, 0.536f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)
+ .inOrder();
}
@Test
@@ -43,6 +45,7 @@
tintController.setMatrix(100);
final float[] matrix = new float[16];
Matrix.setIdentityM(matrix, 0);
- assertThat(tintController.getMatrix()).hasValuesWithin(0.00001f).of(matrix);
+ assertThat(tintController.getMatrix()).usingTolerance(0.00001f)
+ .containsExactly(matrix).inOrder();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index dd4c081..c60d5fb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -95,14 +95,14 @@
/** Tests for {@link HdmiCecController#allocateLogicalAddress} */
@Test
- public void testAllocatLogicalAddress_TvDevicePreferredNotOcupied() {
+ public void testAllocateLogicalAddress_TvDevicePreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_TV, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_TV, mLogicalAddress);
}
@Test
- public void testAllocatLogicalAddress_TvDeviceNonPreferredNotOcupied() {
+ public void testAllocateLogicalAddress_TvDeviceNonPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -110,7 +110,7 @@
}
@Test
- public void testAllocatLogicalAddress_TvDeviceNonPreferredFirstOcupied() {
+ public void testAllocateLogicalAddress_TvDeviceNonPreferredFirstOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -118,7 +118,7 @@
}
@Test
- public void testAllocatLogicalAddress_TvDeviceNonPreferredAllOcupied() {
+ public void testAllocateLogicalAddress_TvDeviceNonPreferredAllOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_TV, SendMessageResult.SUCCESS);
mNativeWrapper.setPollAddressResponse(ADDR_SPECIFIC_USE, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_UNREGISTERED, mCallback);
@@ -127,7 +127,7 @@
}
@Test
- public void testAllocatLogicalAddress_AudioSystemNonPreferredNotOcupied() {
+ public void testAllocateLogicalAddress_AudioSystemNonPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(
DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -135,7 +135,7 @@
}
@Test
- public void testAllocatLogicalAddress_AudioSystemNonPreferredAllOcupied() {
+ public void testAllocateLogicalAddress_AudioSystemNonPreferredAllOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(
DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
@@ -144,14 +144,14 @@
}
@Test
- public void testAllocatLogicalAddress_PlaybackPreferredNotOccupied() {
+ public void testAllocateLogicalAddress_PlaybackPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
}
@Test
- public void testAllocatLogicalAddress_PlaybackPreferredOcuppied() {
+ public void testAllocateLogicalAddress_PlaybackPreferredOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
mTestLooper.dispatchAll();
@@ -159,14 +159,14 @@
}
@Test
- public void testAllocatLogicalAddress_PlaybackNoPreferredNotOcuppied() {
+ public void testAllocateLogicalAddress_PlaybackNoPreferredNotOccupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_1, mLogicalAddress);
}
@Test
- public void testAllocatLogicalAddress_PlaybackNoPreferredFirstOcuppied() {
+ public void testAllocateLogicalAddress_PlaybackNoPreferredFirstOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
@@ -174,7 +174,7 @@
}
@Test
- public void testAllocatLogicalAddress_PlaybackNonPreferredFirstTwoOcuppied() {
+ public void testAllocateLogicalAddress_PlaybackNonPreferredFirstTwoOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_UNREGISTERED, mCallback);
@@ -183,7 +183,7 @@
}
@Test
- public void testAllocatLogicalAddress_PlaybackNonPreferredAllOcupied() {
+ public void testAllocateLogicalAddress_PlaybackNonPreferredAllOccupied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_3, SendMessageResult.SUCCESS);
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
index dbaad66..631b8d3 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
@@ -18,7 +18,6 @@
import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
import static android.location.timezone.LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
-import static com.android.server.location.timezone.ControllerImpl.UNCERTAINTY_DELAY;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DISABLED;
import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_ENABLED;
import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
@@ -47,6 +46,7 @@
import org.junit.Before;
import org.junit.Test;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -92,7 +92,8 @@
mTestLocationTimeZoneProvider.assertInitialized();
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertNextQueueItemIsDelayed(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
}
@@ -121,7 +122,8 @@
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestCallback.assertNoSuggestionMade();
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
// Simulate time passing with no event being received.
mTestThreadingDomain.executeNext();
@@ -140,7 +142,8 @@
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate a location event being received by the provider. This should cause a suggestion
@@ -163,7 +166,8 @@
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate a location event being received by the provider. This should cause a suggestion
@@ -203,7 +207,8 @@
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate a location event being received by the provider. This should cause a suggestion
@@ -216,15 +221,16 @@
mTestCallback.assertSuggestionMadeAndCommit(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT1.getTimeZoneIds());
- // Uncertainty should cause
+ // Uncertainty should cause a suggestion to (only) be queued.
mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ mTestThreadingDomain.assertSingleDelayedQueueItem(testEnvironment.getUncertaintyDelay());
mTestCallback.assertNoSuggestionMade();
- // And a third event should cause yet another suggestion.
+ // And a third event should cause yet another suggestion and for the queued item to be
+ // removed.
mTestLocationTimeZoneProvider.simulateLocationTimeZoneEvent(
USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2);
@@ -250,7 +256,8 @@
testEnvironment.simulateConfigChange(USER1_CONFIG_GEO_DETECTION_ENABLED);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertNextQueueItemIsDelayed(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Now signal a config change so that geo detection is disabled.
@@ -270,7 +277,8 @@
controllerImpl.initialize(testEnvironment, mTestCallback);
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate a location event being received by the provider. This should cause a suggestion
@@ -304,7 +312,8 @@
// There should be a runnable scheduled to suggest uncertainty if no event is received.
mTestLocationTimeZoneProvider.assertIsEnabled(USER1_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ Duration expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Have the provider suggest a time zone.
@@ -328,7 +337,8 @@
int[] expectedStateTransitions = { PROVIDER_STATE_DISABLED, PROVIDER_STATE_ENABLED };
mTestLocationTimeZoneProvider.assertStateChangesAndCommit(expectedStateTransitions);
mTestLocationTimeZoneProvider.assertConfig(USER2_CONFIG_GEO_DETECTION_ENABLED);
- mTestThreadingDomain.assertSingleDelayedQueueItem(UNCERTAINTY_DELAY);
+ expectedTimeout = expectedProviderInitializationTimeout();
+ mTestThreadingDomain.assertSingleDelayedQueueItem(expectedTimeout);
mTestCallback.assertNoSuggestionMade();
// Simulate no event being received, and time passing.
@@ -351,8 +361,18 @@
return builder.build();
}
+
+ private Duration expectedProviderInitializationTimeout() {
+ return TestEnvironment.PROVIDER_INITIALIZATION_TIMEOUT
+ .plus(TestEnvironment.PROVIDER_INITIALIZATION_TIMEOUT_FUZZ);
+ }
+
private static class TestEnvironment extends LocationTimeZoneProviderController.Environment {
+ static final Duration PROVIDER_INITIALIZATION_TIMEOUT = Duration.ofMinutes(5);
+ static final Duration PROVIDER_INITIALIZATION_TIMEOUT_FUZZ = Duration.ofMinutes(1);
+ private static final Duration UNCERTAINTY_DELAY = Duration.ofMinutes(3);
+
private final LocationTimeZoneProviderController mController;
private ConfigurationInternal mConfigurationInternal;
@@ -369,6 +389,21 @@
return mConfigurationInternal;
}
+ @Override
+ Duration getProviderInitializationTimeout() {
+ return PROVIDER_INITIALIZATION_TIMEOUT;
+ }
+
+ @Override
+ Duration getProviderInitializationTimeoutFuzz() {
+ return PROVIDER_INITIALIZATION_TIMEOUT_FUZZ;
+ }
+
+ @Override
+ Duration getUncertaintyDelay() {
+ return UNCERTAINTY_DELAY;
+ }
+
void simulateConfigChange(ConfigurationInternal newConfig) {
ConfigurationInternal oldConfig = mConfigurationInternal;
mConfigurationInternal = Objects.requireNonNull(newConfig);
@@ -432,7 +467,7 @@
}
@Override
- void onEnable() {
+ void onEnable(Duration initializationTimeout) {
// Nothing needed for tests.
}
@@ -464,7 +499,7 @@
/**
* Asserts the provider's config matches the expected, and the current state is set
- * accordinly. Commits the latest changes to the state.
+ * accordingly. Commits the latest changes to the state.
*/
void assertIsEnabled(@NonNull ConfigurationInternal expectedConfig) {
assertConfig(expectedConfig);
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
index 7c882fc..5542db0 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
@@ -34,6 +34,8 @@
import org.junit.Before;
import org.junit.Test;
+import java.time.Duration;
+
/**
* Tests for {@link NullLocationTimeZoneProvider} and, indirectly, the class it extends
* {@link LocationTimeZoneProvider}.
@@ -73,7 +75,8 @@
provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
- provider.enable(config);
+ Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
+ provider.enable(config, arbitraryInitializationTimeout);
// The StubbedProvider should enters enabled state, but immediately schedule a runnable to
// switch to perm failure.
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
index 70ff22d..def919e 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
@@ -105,8 +105,8 @@
assertTrue(getNextQueueItemDelayMillis() == 0);
}
- void assertNextQueueItemIsDelayed(Duration expectedDelay) {
- assertTrue(getNextQueueItemDelayMillis() == expectedDelay.toMillis());
+ private void assertNextQueueItemIsDelayed(Duration expectedDelay) {
+ assertEquals(getNextQueueItemDelayMillis(), expectedDelay.toMillis());
}
void assertQueueEmpty() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index dac0542..bc74783 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -62,6 +62,7 @@
import com.android.server.LocalServices;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.PermissionSettings;
import com.google.common.truth.Truth;
@@ -94,6 +95,8 @@
PermissionSettings mPermissionSettings;
@Mock
RuntimePermissionsPersistence mRuntimePermissionsPersistence;
+ @Mock
+ LegacyPermissionDataProvider mPermissionDataProvider;
@Before
public void initializeMocks() {
@@ -115,7 +118,7 @@
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
verifyKeySetMetaData(settings);
}
@@ -129,7 +132,7 @@
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
// write out, read back in and verify the same
@@ -145,7 +148,7 @@
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
assertThat(settings.getPackageLPr(PACKAGE_NAME_3), is(notNullValue()));
assertThat(settings.getPackageLPr(PACKAGE_NAME_1), is(notNullValue()));
@@ -167,13 +170,13 @@
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
settings.writeLPr();
// Create Settings again to make it read from the new files
settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
PackageSetting ps = settings.getPackageLPr(PACKAGE_NAME_2);
@@ -196,7 +199,8 @@
writePackageRestrictions_noSuspendingPackageXml(0);
final Object lock = new Object();
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock);
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
+ lock);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, createPackageSetting(PACKAGE_NAME_2));
settingsUnderTest.readPackageRestrictionsLPr(0);
@@ -219,7 +223,8 @@
writePackageRestrictions_noSuspendParamsMapXml(0);
final Object lock = new Object();
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, lock);
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
+ lock);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, createPackageSetting(PACKAGE_NAME_1));
settingsUnderTest.readPackageRestrictionsLPr(0);
@@ -246,7 +251,7 @@
@Test
public void testReadWritePackageRestrictions_suspendInfo() {
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
new Object());
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
@@ -344,7 +349,7 @@
@Test
public void testReadWritePackageRestrictions_distractionFlags() {
final Context context = InstrumentationRegistry.getTargetContext();
- final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null,
+ final Settings settingsUnderTest = new Settings(context.getFilesDir(), null, null, null,
new Object());
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
@@ -389,7 +394,7 @@
final Context context = InstrumentationRegistry.getTargetContext();
final Object lock = new Object();
final Settings settingsUnderTest = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
ps1.appId = Process.FIRST_APPLICATION_UID;
ps1.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
@@ -465,7 +470,7 @@
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
Settings settings = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
assertThat(settings.readLPw(createFakeUsers()), is(true));
// Enable/Disable a package
@@ -638,7 +643,7 @@
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
final PackageSetting testPkgSetting01 =
@@ -748,7 +753,7 @@
final Context context = InstrumentationRegistry.getContext();
final Object lock = new Object();
final Settings testSettings01 = new Settings(context.getFilesDir(), mPermissionSettings,
- mRuntimePermissionsPersistence, lock);
+ mRuntimePermissionsPersistence, mPermissionDataProvider, lock);
final SharedUserSetting testUserSetting01 = createSharedUserSetting(
testSettings01, "TestUser", 10064, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/);
final PackageSetting testPkgSetting01 = Settings.createNewSetting(
diff --git a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
index a550b27..f1930d7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SELinuxMMACTest.java
@@ -44,7 +44,8 @@
public class SELinuxMMACTest {
private static final String PACKAGE_NAME = "my.package";
- private static final int OPT_IN_VERSION = Build.VERSION_CODES.R;
+ private static final int LATEST_OPT_IN_VERSION = Build.VERSION_CODES.S;
+ private static final int R_OPT_IN_VERSION = Build.VERSION_CODES.R;
@Mock
PlatformCompat mMockCompatibility;
@@ -56,7 +57,17 @@
argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
.thenReturn(true);
assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
- is("default:targetSdkVersion=" + OPT_IN_VERSION));
+ is("default:targetSdkVersion=" + LATEST_OPT_IN_VERSION));
+ }
+
+ @Test
+ public void getSeInfoOptInToR() {
+ AndroidPackage pkg = makePackage(Build.VERSION_CODES.P);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + R_OPT_IN_VERSION));
}
@Test
@@ -70,13 +81,33 @@
}
@Test
- public void getSeInfoNoOptInButAlreadyR() {
- AndroidPackage pkg = makePackage(OPT_IN_VERSION);
+ public void getSeInfoNoOptInButAlreadyLatest() {
+ AndroidPackage pkg = makePackage(LATEST_OPT_IN_VERSION);
when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_LATEST_CHANGES),
argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
.thenReturn(false);
assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
- is("default:targetSdkVersion=" + OPT_IN_VERSION));
+ is("default:targetSdkVersion=" + LATEST_OPT_IN_VERSION));
+ }
+
+ @Test
+ public void getSeInfoNoOptInButAlreadyR() {
+ AndroidPackage pkg = makePackage(R_OPT_IN_VERSION);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(false);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + R_OPT_IN_VERSION));
+ }
+
+ @Test
+ public void getSeInfoOptInRButLater() {
+ AndroidPackage pkg = makePackage(R_OPT_IN_VERSION + 1);
+ when(mMockCompatibility.isChangeEnabledInternal(eq(SELinuxMMAC.SELINUX_R_CHANGES),
+ argThat(argument -> argument.packageName.equals(pkg.getPackageName()))))
+ .thenReturn(true);
+ assertThat(SELinuxMMAC.getSeInfo(pkg, null, mMockCompatibility),
+ is("default:targetSdkVersion=" + (R_OPT_IN_VERSION + 1)));
}
private AndroidPackage makePackage(int targetSdkVersion) {
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index ebcf10d..509eb25 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -59,6 +59,7 @@
import android.os.HwParcel;
import android.os.IHwBinder;
import android.os.IHwInterface;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.system.ErrnoException;
@@ -126,7 +127,7 @@
model.uuid = "12345678-2345-3456-4567-abcdef987654";
model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
byte[] data = new byte[]{91, 92, 93, 94, 95};
- model.data = byteArrayToFileDescriptor(data);
+ model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
model.dataSize = data.length;
return model;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 9e7226e7..ed6a20b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -69,10 +69,12 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -102,10 +104,12 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.app.usage.UsageStatsManagerInternal;
import android.companion.ICompanionDeviceManager;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
@@ -182,6 +186,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
@@ -282,6 +287,7 @@
NotificationHistoryManager mHistoryManager;
@Mock
StatsManager mStatsManager;
+ BroadcastReceiver mPackageIntentReceiver;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -480,6 +486,28 @@
mShortcutHelper.setLauncherApps(mLauncherApps);
mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal);
+ // Capture PackageIntentReceiver
+ ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ ArgumentCaptor<IntentFilter> intentFilterCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+
+ verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(),
+ any(), intentFilterCaptor.capture(), any(), any());
+ List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues();
+ List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues();
+
+ for (int i = 0; i < intentFilters.size(); i++) {
+ final IntentFilter filter = intentFilters.get(i);
+ if (filter.hasAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED)
+ && filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED)
+ && filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) {
+ mPackageIntentReceiver = broadcastReceivers.get(i);
+ break;
+ }
+ }
+ assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
+
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
ShortcutInfo info = mock(ShortcutInfo.class);
@@ -526,11 +554,48 @@
public void tearDown() throws Exception {
if (mFile != null) mFile.delete();
clearDeviceConfig();
- mService.unregisterDeviceConfigChange();
+
+ try {
+ mService.onDestroy();
+ } catch (IllegalStateException e) {
+ // can throw if a broadcast receiver was never registered
+ }
+
InstrumentationRegistry.getInstrumentation()
.getUiAutomation().dropShellPermissionIdentity();
}
+ private void simulatePackageSuspendBroadcast(boolean suspend, String pkg,
+ int uid) {
+ // mimics receive broadcast that package is (un)suspended
+ // but does not actually (un)suspend the package
+ final Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+ new String[]{pkg});
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, new int[]{uid});
+
+ final String action = suspend ? Intent.ACTION_PACKAGES_SUSPENDED
+ : Intent.ACTION_PACKAGES_UNSUSPENDED;
+ final Intent intent = new Intent(action);
+ intent.putExtras(extras);
+
+ mPackageIntentReceiver.onReceive(getContext(), intent);
+ }
+
+ private void simulatePackageDistractionBroadcast(int flag, String[] pkgs, int[] uids) {
+ // mimics receive broadcast that package is (un)distracting
+ // but does not actually register that info with packagemanager
+ final Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgs);
+ extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, flag);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uids);
+
+ final Intent intent = new Intent(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED);
+ intent.putExtras(extras);
+
+ mPackageIntentReceiver.onReceive(getContext(), intent);
+ }
+
private ArrayMap<Boolean, ArrayList<ComponentName>> generateResetComponentValues() {
ArrayMap<Boolean, ArrayList<ComponentName>> changed = new ArrayMap<>();
changed.put(true, new ArrayList<>());
@@ -2533,10 +2598,11 @@
@Test
public void testHasCompanionDevice_noService() {
- mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
+ NotificationManagerService noManService =
+ new TestableNotificationManagerService(mContext, mNotificationRecordLogger,
mNotificationInstanceIdSequence);
- assertFalse(mService.hasCompanionDevice(mListener));
+ assertFalse(noManService.hasCompanionDevice(mListener));
}
@Test
@@ -4347,13 +4413,13 @@
mService.addNotification(notif2);
// on broadcast, hide the 2 notifications
- mService.simulatePackageSuspendBroadcast(true, PKG);
+ simulatePackageSuspendBroadcast(true, PKG, notif1.getUid());
ArgumentCaptor<List> captorHide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
assertEquals(2, captorHide.getValue().size());
// on broadcast, unhide the 2 notifications
- mService.simulatePackageSuspendBroadcast(false, PKG);
+ simulatePackageSuspendBroadcast(false, PKG, notif1.getUid());
ArgumentCaptor<List> captorUnhide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
assertEquals(2, captorUnhide.getValue().size());
@@ -4370,7 +4436,24 @@
mService.addNotification(notif2);
// on broadcast, nothing is hidden since no notifications are of package "test_package"
- mService.simulatePackageSuspendBroadcast(true, "test_package");
+ simulatePackageSuspendBroadcast(true, "test_package", notif1.getUid());
+ ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+ verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
+ assertEquals(0, captor.getValue().size());
+ }
+
+ @Test
+ public void testNotificationFromDifferentUserHidden() {
+ // post 2 notification from this package
+ final NotificationRecord notif1 = generateNotificationRecord(
+ mTestNotificationChannel, 1, null, true);
+ final NotificationRecord notif2 = generateNotificationRecord(
+ mTestNotificationChannel, 2, null, false);
+ mService.addNotification(notif1);
+ mService.addNotification(notif2);
+
+ // on broadcast, nothing is hidden since no notifications are of user 10 with package PKG
+ simulatePackageSuspendBroadcast(true, PKG, 10);
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
assertEquals(0, captor.getValue().size());
@@ -4387,16 +4470,18 @@
mService.addNotification(pkgB);
// on broadcast, hide one of the packages
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a"},
+ new int[] {1000});
ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captorHide.capture());
assertEquals(1, captorHide.getValue().size());
assertEquals("a", captorHide.getValue().get(0).getSbn().getPackageName());
// on broadcast, unhide the package
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a"},
+ new int[] {1000});
ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyUnhiddenLocked(captorUnhide.capture());
assertEquals(1, captorUnhide.getValue().size());
@@ -4414,8 +4499,9 @@
mService.addNotification(pkgB);
// on broadcast, hide one of the packages
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"a", "b"},
+ new int[] {1000, 1001});
ArgumentCaptor<List<NotificationRecord>> captorHide = ArgumentCaptor.forClass(List.class);
// should be called only once.
@@ -4425,8 +4511,9 @@
assertEquals("b", captorHide.getValue().get(1).getSbn().getPackageName());
// on broadcast, unhide the package
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS, new String[] {"a", "b"},
+ new int[] {1000, 1001});
ArgumentCaptor<List<NotificationRecord>> captorUnhide = ArgumentCaptor.forClass(List.class);
// should be called only once.
@@ -4444,8 +4531,9 @@
mService.addNotification(notif1);
// on broadcast, nothing is hidden since no notifications are of package "test_package"
- mService.simulatePackageDistractionBroadcast(
- PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"});
+ simulatePackageDistractionBroadcast(
+ PackageManager.RESTRICTION_HIDE_NOTIFICATIONS, new String[] {"test_package"},
+ new int[]{notif1.getUid()});
ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
assertEquals(0, captor.getValue().size());
@@ -7011,4 +7099,39 @@
assertTrue(mService.isVisibleToListener(sbn, info));
}
+ @Test
+ public void testUserInitiatedCancelAll_groupCancellationOrder_groupPostedFirst() {
+ final NotificationRecord parent = spy(generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true));
+ final NotificationRecord child = spy(generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false));
+ mService.addNotification(parent);
+ mService.addNotification(child);
+
+ InOrder inOrder = inOrder(parent, child);
+
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ inOrder.verify(parent).recordDismissalSentiment(anyInt());
+ inOrder.verify(child).recordDismissalSentiment(anyInt());
+ }
+
+ @Test
+ public void testUserInitiatedCancelAll_groupCancellationOrder_groupPostedSecond() {
+ final NotificationRecord parent = spy(generateNotificationRecord(
+ mTestNotificationChannel, 1, "group", true));
+ final NotificationRecord child = spy(generateNotificationRecord(
+ mTestNotificationChannel, 2, "group", false));
+ mService.addNotification(child);
+ mService.addNotification(parent);
+
+ InOrder inOrder = inOrder(parent, child);
+
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ parent.getUserId());
+ waitForIdle();
+ inOrder.verify(parent).recordDismissalSentiment(anyInt());
+ inOrder.verify(child).recordDismissalSentiment(anyInt());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 89a0c7c..f10cab8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -550,7 +550,7 @@
final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
try {
doReturn(false).when(stack).isTranslucent(any());
- assertFalse(mStack.shouldBeVisible(null /* starting */));
+ assertTrue(mStack.shouldBeVisible(null /* starting */));
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 076047b..e478819 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -63,6 +63,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -670,7 +671,7 @@
doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
// caller is temp allowed
if (callerIsTempAllowed) {
- callerApp.addAllowBackgroundActivityStartsToken(new Binder(), null);
+ callerApp.addOrUpdateAllowBackgroundActivityStartsToken(new Binder(), null);
}
// caller is instrumenting with background activity starts privileges
callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
@@ -800,6 +801,7 @@
final Task topTask = new TaskBuilder(mSupervisor).setParentTask(topStack).build();
new ActivityBuilder(mAtm).setTask(topTask).build();
+ doReturn(mActivityMetricsLogger).when(mSupervisor).getActivityMetricsLogger();
// Start activity with the same intent as {@code singleTaskActivity} on secondary display.
final ActivityOptions options = ActivityOptions.makeBasic()
.setLaunchDisplayId(secondaryDisplay.mDisplayId);
@@ -813,6 +815,9 @@
// Ensure secondary display only creates two stacks.
verify(secondaryTaskContainer, times(2)).createStack(anyInt(), anyInt(), anyBoolean());
+ // The metrics logger should receive the same result and non-null options.
+ verify(mActivityMetricsLogger).notifyActivityLaunched(any() /* launchingState */,
+ eq(result), eq(singleTaskActivity), notNull() /* options */);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
index f4b50dc..54b2b3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java
@@ -18,6 +18,8 @@
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -39,6 +41,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/**
+ * Build/Install/Run:
+ * atest WmTests:DisplayAreaOrganizerTest
+ */
@SmallTest
@Presubmit
@RunWith(WindowTestRunner.class)
@@ -61,14 +67,22 @@
}
private IDisplayAreaOrganizer registerMockOrganizer(int feature) {
- final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
- when(organizer.asBinder()).thenReturn(new Binder());
+ return registerMockOrganizer(feature, new Binder());
+ }
+ private IDisplayAreaOrganizer registerMockOrganizer(int feature, Binder binder) {
+ final IDisplayAreaOrganizer organizer = createMockOrganizer(binder);
mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
.registerOrganizer(organizer, feature);
return organizer;
}
+ private IDisplayAreaOrganizer createMockOrganizer(Binder binder) {
+ final IDisplayAreaOrganizer organizer = mock(IDisplayAreaOrganizer.class);
+ when(organizer.asBinder()).thenReturn(binder);
+ return organizer;
+ }
+
private void unregisterMockOrganizer(IDisplayAreaOrganizer organizer) {
mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
.unregisterOrganizer(organizer);
@@ -99,4 +113,16 @@
// Ensure it was still only called once if the bounds didn't change
verify(organizer).onDisplayAreaInfoChanged(any());
}
+
+ @Test
+ public void testUnregisterOrganizer() {
+ final Binder binder = new Binder();
+ registerMockOrganizer(FEATURE_VENDOR_FIRST, binder);
+
+ assertThat(mTestDisplayArea.mOrganizer).isNotNull();
+
+ unregisterMockOrganizer(createMockOrganizer(binder));
+
+ assertThat(mTestDisplayArea.mOrganizer).isNull();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 89a34cf..633d216 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1163,6 +1163,8 @@
mDisplayContent.getDisplayRotation().setRotation(ROTATION_0);
mDisplayContent.computeScreenConfiguration(config);
mDisplayContent.onRequestedOverrideConfigurationChanged(config);
+ assertNotEquals(config90.windowConfiguration.getMaxBounds(),
+ config.windowConfiguration.getMaxBounds());
final ActivityRecord app = mAppWindow.mActivityRecord;
app.setVisible(false);
@@ -1218,8 +1220,9 @@
verify(t, never()).setPosition(any(), eq(0), eq(0));
// Launch another activity before the transition is finished.
- final ActivityRecord app2 = new TaskBuilder(mSupervisor)
- .setDisplay(mDisplayContent).setCreateActivity(true).build().getTopMostActivity();
+ final Task task2 = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
+ final ActivityRecord app2 = new ActivityBuilder(mAtm).setStack(task2)
+ .setUseProcess(app.app).build();
app2.setVisible(false);
mDisplayContent.mOpeningApps.add(app2);
app2.setRequestedOrientation(newOrientation);
@@ -1229,6 +1232,12 @@
assertTrue(app.hasFixedRotationTransform(app2));
assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2));
+ final Configuration expectedProcConfig = new Configuration(app2.app.getConfiguration());
+ expectedProcConfig.windowConfiguration.setActivityType(
+ WindowConfiguration.ACTIVITY_TYPE_UNDEFINED);
+ assertEquals("The process should receive rotated configuration for compatibility",
+ expectedProcConfig, app2.app.getConfiguration());
+
// The fixed rotation transform can only be finished when all animation finished.
doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 9511181..2920c1d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -20,6 +20,8 @@
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.TOP;
+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;
@@ -218,10 +220,15 @@
@Test
public void addingWindow_throwsException_WithMultipleInsetTypes() {
- WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
- win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
+ WindowState win1 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+ win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
- expectThrows(IllegalArgumentException.class, () -> addWindow(win));
+ expectThrows(IllegalArgumentException.class, () -> addWindow(win1));
+
+ WindowState win2 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
+ win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR};
+
+ expectThrows(IllegalArgumentException.class, () -> addWindow(win2));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
similarity index 60%
rename from services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
rename to services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
index f53894a..c3e1922 100644
--- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateBlacklistTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
@@ -40,107 +40,107 @@
/**
* Build/Install/Run:
- * atest WmTests:HighRefreshRateBlacklistTest
+ * atest WmTests:HighRefreshRateDenylistTest
*/
@SmallTest
@Presubmit
-public class HighRefreshRateBlacklistTest {
+public class HighRefreshRateDenylistTest {
private static final String APP1 = "com.android.sample1";
private static final String APP2 = "com.android.sample2";
private static final String APP3 = "com.android.sample3";
- private HighRefreshRateBlacklist mBlacklist;
+ private HighRefreshRateDenylist mDenylist;
@After
public void tearDown() {
- mBlacklist.dispose();
+ mDenylist.dispose();
}
@Test
- public void testDefaultBlacklist() {
+ public void testDefaultDenylist() {
final Resources r = createResources(APP1, APP2);
- mBlacklist = new HighRefreshRateBlacklist(r, new FakeDeviceConfig());
+ mDenylist = new HighRefreshRateDenylist(r, new FakeDeviceConfig());
- assertTrue(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertFalse(mBlacklist.isBlacklisted(APP3));
+ assertTrue(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertFalse(mDenylist.isDenylisted(APP3));
}
@Test
- public void testNoDefaultBlacklist() {
+ public void testNoDefaultDenylist() {
final Resources r = createResources();
- mBlacklist = new HighRefreshRateBlacklist(r, new FakeDeviceConfig());
+ mDenylist = new HighRefreshRateDenylist(r, new FakeDeviceConfig());
- assertFalse(mBlacklist.isBlacklisted(APP1));
+ assertFalse(mDenylist.isDenylisted(APP1));
}
@Test
- public void testDefaultBlacklistIsOverriddenByDeviceConfig() {
+ public void testDefaultDenylistIsOverriddenByDeviceConfig() {
final Resources r = createResources(APP1);
final FakeDeviceConfig config = new FakeDeviceConfig();
- config.setBlacklist(APP2 + "," + APP3);
- mBlacklist = new HighRefreshRateBlacklist(r, config);
+ config.setDenylist(APP2 + "," + APP3);
+ mDenylist = new HighRefreshRateDenylist(r, config);
- assertFalse(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertTrue(mBlacklist.isBlacklisted(APP3));
+ assertFalse(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertTrue(mDenylist.isDenylisted(APP3));
}
@Test
- public void testDefaultBlacklistIsOverriddenByEmptyDeviceConfig() {
+ public void testDefaultDenylistIsOverriddenByEmptyDeviceConfig() {
final Resources r = createResources(APP1);
final FakeDeviceConfig config = new FakeDeviceConfig();
- config.setBlacklist("");
- mBlacklist = new HighRefreshRateBlacklist(r, config);
+ config.setDenylist("");
+ mDenylist = new HighRefreshRateDenylist(r, config);
- assertFalse(mBlacklist.isBlacklisted(APP1));
+ assertFalse(mDenylist.isDenylisted(APP1));
}
@Test
- public void testDefaultBlacklistIsOverriddenByDeviceConfigUpdate() {
+ public void testDefaultDenylistIsOverriddenByDeviceConfigUpdate() {
final Resources r = createResources(APP1);
final FakeDeviceConfig config = new FakeDeviceConfig();
- mBlacklist = new HighRefreshRateBlacklist(r, config);
+ mDenylist = new HighRefreshRateDenylist(r, config);
// First check that the default denylist is in effect
- assertTrue(mBlacklist.isBlacklisted(APP1));
- assertFalse(mBlacklist.isBlacklisted(APP2));
- assertFalse(mBlacklist.isBlacklisted(APP3));
+ assertTrue(mDenylist.isDenylisted(APP1));
+ assertFalse(mDenylist.isDenylisted(APP2));
+ assertFalse(mDenylist.isDenylisted(APP3));
// Then confirm that the DeviceConfig list has propagated and taken effect.
- config.setBlacklist(APP2 + "," + APP3);
- assertFalse(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertTrue(mBlacklist.isBlacklisted(APP3));
+ config.setDenylist(APP2 + "," + APP3);
+ assertFalse(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertTrue(mDenylist.isDenylisted(APP3));
// Finally make sure we go back to the default list if the DeviceConfig gets deleted.
- config.setBlacklist(null);
- assertTrue(mBlacklist.isBlacklisted(APP1));
- assertFalse(mBlacklist.isBlacklisted(APP2));
- assertFalse(mBlacklist.isBlacklisted(APP3));
+ config.setDenylist(null);
+ assertTrue(mDenylist.isDenylisted(APP1));
+ assertFalse(mDenylist.isDenylisted(APP2));
+ assertFalse(mDenylist.isDenylisted(APP3));
}
@Test
public void testOverriddenByDeviceConfigUnrelatedFlagChanged() {
final Resources r = createResources(APP1);
final FakeDeviceConfig config = new FakeDeviceConfig();
- mBlacklist = new HighRefreshRateBlacklist(r, config);
- config.setBlacklist(APP2 + "," + APP3);
- assertFalse(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertTrue(mBlacklist.isBlacklisted(APP3));
+ mDenylist = new HighRefreshRateDenylist(r, config);
+ config.setDenylist(APP2 + "," + APP3);
+ assertFalse(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertTrue(mDenylist.isDenylisted(APP3));
// Change an unrelated flag in our namespace and verify that the denylist is intact
config.putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, "someKey", "someValue");
- assertFalse(mBlacklist.isBlacklisted(APP1));
- assertTrue(mBlacklist.isBlacklisted(APP2));
- assertTrue(mBlacklist.isBlacklisted(APP3));
+ assertFalse(mDenylist.isDenylisted(APP1));
+ assertTrue(mDenylist.isDenylisted(APP2));
+ assertTrue(mDenylist.isDenylisted(APP3));
}
- private Resources createResources(String... defaultBlacklist) {
+ private Resources createResources(String... defaultDenylist) {
Resources r = mock(Resources.class);
when(r.getStringArray(R.array.config_highRefreshRateBlacklist))
- .thenReturn(defaultBlacklist);
+ .thenReturn(defaultDenylist);
return r;
}
@@ -160,9 +160,9 @@
super.addOnPropertiesChangedListener(namespace, executor, listener);
}
- void setBlacklist(String blacklist) {
+ void setDenylist(String denylist) {
putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- KEY_HIGH_REFRESH_RATE_BLACKLIST, blacklist);
+ KEY_HIGH_REFRESH_RATE_BLACKLIST, denylist);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 4a49743..e2cd8a9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -20,6 +20,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
+import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -284,12 +286,17 @@
public void testBarControllingWinChanged() {
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState climateBar = createWindow(null, TYPE_APPLICATION, "climateBar");
+ final WindowState extraNavBar = createWindow(null, TYPE_APPLICATION, "extraNavBar");
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ getController().getSourceProvider(ITYPE_CLIMATE_BAR).setWindow(climateBar, null, null);
+ getController().getSourceProvider(ITYPE_EXTRA_NAVIGATION_BAR).setWindow(extraNavBar, null,
+ null);
getController().onBarControlTargetChanged(app, null, app, null);
InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
- assertEquals(2, controls.length);
+ assertEquals(4, controls.length);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index e887be0..77a4b05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -48,7 +48,7 @@
private static final int LOW_MODE_ID = 3;
private RefreshRatePolicy mPolicy;
- private HighRefreshRateBlacklist mBlacklist = mock(HighRefreshRateBlacklist.class);
+ private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class);
@Before
public void setUp() {
@@ -61,7 +61,7 @@
defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60),
};
di.defaultModeId = 1;
- mPolicy = new RefreshRatePolicy(mWm, di, mBlacklist);
+ mPolicy = new RefreshRatePolicy(mWm, di, mDenylist);
}
@Test
@@ -81,7 +81,7 @@
final WindowState blacklistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
"blacklistedWindow");
blacklistedWindow.mAttrs.packageName = "com.android.test";
- when(mBlacklist.isBlacklisted("com.android.test")).thenReturn(true);
+ when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(blacklistedWindow));
}
@@ -90,7 +90,7 @@
final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION,
"overrideWindow");
overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
- when(mBlacklist.isBlacklisted("com.android.test")).thenReturn(true);
+ when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index ef74861..25ba6db3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -68,6 +68,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import java.util.ArrayList;
@@ -142,6 +143,9 @@
assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness);
}
+ // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported.
+ // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon.
+ @Ignore
@Test
public void testMultipleDecors() {
// Test 2 decor windows on-top.
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index da3319a..6f5389d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -236,6 +236,7 @@
inputChannels[0].dispose();
mInputChannel = inputChannels[1];
doReturn(mInputChannel).when(mImService).monitorInput(anyString(), anyInt());
+ doReturn(mInputChannel).when(mImService).createInputChannel(anyString());
// StatusBarManagerInternal
final StatusBarManagerInternal sbmi = mock(StatusBarManagerInternal.class);
@@ -337,7 +338,7 @@
// HighRefreshRateBlacklist with DeviceConfig. We need to undo that here to avoid
// leaking mWmService.
mWmService.mConstants.dispose();
- mWmService.mHighRefreshRateBlacklist.dispose();
+ mWmService.mHighRefreshRateDenylist.dispose();
// This makes sure the posted messages without delay are processed, e.g.
// DisplayPolicy#release, WindowManagerService#setAnimationScale.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index e71f7ec..8b025e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -29,6 +29,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -277,6 +279,24 @@
assertThat(secondTaskDisplayArea.isLastFocused()).isTrue();
}
+ @Test
+ public void testIgnoreOrientationRequest() {
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final Task stack = taskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(stack).build();
+
+ mDisplayContent.setFocusedApp(activity);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+
+ taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+ }
+
private void assertGetOrCreateStack(int windowingMode, int activityType, Task candidateTask,
boolean reuseCandidate) {
final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 11eaf8c..0152fc6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -26,6 +26,9 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -42,6 +45,8 @@
import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -361,6 +366,45 @@
}
@Test
+ public void testSetIgnoreOrientationRequest() {
+ removeGlobalMinSizeRestriction();
+ final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea();
+ final Task stack = taskDisplayArea.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true)
+ .setStack(stack).build();
+ taskDisplayArea.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+ mDisplayContent.setFocusedApp(activity);
+ activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ // TDA returns UNSET when ignoreOrientationRequest == true
+ // DC is UNSPECIFIED because it is using the previous (default) when TDA returns UNSET.
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED);
+
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ t.setIgnoreOrientationRequest(
+ taskDisplayArea.mRemoteToken.toWindowContainerToken(),
+ false /* ignoreOrientationRequest */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+
+ // TDA returns app request orientation when ignoreOrientationRequest == false
+ // DC uses the same as TDA returns when it is not UNSET.
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+
+ t.setIgnoreOrientationRequest(
+ taskDisplayArea.mRemoteToken.toWindowContainerToken(),
+ true /* ignoreOrientationRequest */);
+ mWm.mAtmService.mWindowOrganizerController.applyTransaction(t);
+
+ // TDA returns UNSET when ignoreOrientationRequest == true
+ // DC is LANDSCAPE because it is using the previous when TDA returns UNSET.
+ assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET);
+ assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE);
+ }
+
+ @Test
public void testOverrideConfigSize() {
removeGlobalMinSizeRestriction();
final Task stack = new TaskBuilder(mSupervisor)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 189e540..38c7531 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -266,6 +266,15 @@
mWpc.onMergedOverrideConfigurationChanged(config);
assertEquals(ACTIVITY_TYPE_HOME, config.windowConfiguration.getActivityType());
assertEquals(ACTIVITY_TYPE_UNDEFINED, mWpc.getActivityType());
+
+ final int globalSeq = 100;
+ mRootWindowContainer.getConfiguration().seq = globalSeq;
+ invertOrientation(mWpc.getConfiguration());
+ new ActivityBuilder(mAtm).setCreateTask(true).setUseProcess(mWpc).build();
+
+ assertTrue(mWpc.registeredForActivityConfigChanges());
+ assertEquals("Config seq of process should not be affected by activity",
+ mWpc.getConfiguration().seq, globalSeq);
}
private TestDisplayContent createTestDisplayContentInContainer() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0ec2793..a82d988 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1888,8 +1888,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -1941,8 +1941,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2010,8 +2010,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2088,8 +2088,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2126,8 +2126,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2210,8 +2210,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -2247,8 +2247,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -3723,8 +3723,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -3761,8 +3761,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -4015,8 +4015,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
@@ -4054,8 +4054,8 @@
*
* <p>Starting with API level 29, persistent device identifiers are guarded behind additional
* restrictions, and apps are recommended to use resettable identifiers (see <a
- * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
- * the following requirements is met:
+ * href="/training/articles/user-data-ids">Best practices for unique identifiers</a>). This
+ * method can be invoked if one of the following requirements is met:
* <ul>
* <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
* is a privileged permission that can only be granted to apps preloaded on the device.
diff --git a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
index 026677e..99dde85 100644
--- a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
+++ b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -62,13 +63,62 @@
CLog.w("Iteration N" + iteration);
final int userId = createManagedProfile();
startUser(userId);
- installPackageAsUser(DUMMY_DPC_APK, true /* grantPermissions */, userId, "-t");
+ installPackageAsUser(
+ DUMMY_DPC_APK, /* grantPermissions= */true, userId, /* options= */"-t");
setProfileOwner(DUMMY_DPC_COMPONENT, userId);
removeUser(userId);
}
CLog.w("Completed " + iteration + " iterations.");
}
+ /**
+ * Create, start, and kill managed profiles in a loop with waitForBroadcastIdle after each user
+ * operation.
+ */
+ @Test
+ public void testCreateStartDeleteStable() throws Exception {
+ // Disable package verifier for ADB installs.
+ getDevice().executeShellCommand("settings put global verifier_verify_adb_installs 0");
+ int iteration = 0;
+ final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(TIME_LIMIT_MINUTES);
+ while (System.nanoTime() < deadline) {
+ iteration++;
+ CLog.w("Iteration N" + iteration);
+ final int userId = createManagedProfile();
+ waitForBroadcastIdle();
+
+ startUser(userId);
+ waitForBroadcastIdle();
+
+ installPackageAsUser(
+ DUMMY_DPC_APK, /* grantPermissions= */true, userId, /* options= */"-t");
+
+ setProfileOwner(DUMMY_DPC_COMPONENT, userId);
+
+ removeUser(userId);
+ waitForBroadcastIdle();
+ }
+ CLog.w("Completed " + iteration + " iterations.");
+ }
+
+ private void waitForBroadcastIdle() throws Exception {
+ final CollectingOutputReceiver receiver = new CollectingOutputReceiver();
+ // We allow 8min for the command to complete and 4min for the command to start to
+ // output something.
+ getDevice().executeShellCommand(
+ "am wait-for-broadcast-idle",
+ receiver,
+ /* maxTimeoutForCommand= */8,
+ /* maxTimeoutToOutputShellResponse= */4,
+ TimeUnit.MINUTES,
+ /* retryAttempts= */0);
+ final String output = receiver.getOutput();
+ if (!output.contains("All broadcast queues are idle!")) {
+ CLog.e("Output from 'am wait-for-broadcast-idle': %s", output);
+ fail("'am wait-for-broadcase-idle' did not complete.");
+ }
+ }
+
private int createManagedProfile() throws Exception {
final String output = getDevice().executeShellCommand(
"pm create-user --profileOf 0 --managed TestProfile");
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 9f0b41f..c895420 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -67,6 +67,9 @@
private NetworkAgent mNetworkAgent;
private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
+ // Controls how test network agent is going to wait before responding to keepalive
+ // start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem.
+ private long mKeepaliveResponseDelay = 0L;
private Integer mExpectedKeepaliveSlot = null;
public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
@@ -134,12 +137,17 @@
if (mWrapper.mExpectedKeepaliveSlot != null) {
assertEquals((int) mWrapper.mExpectedKeepaliveSlot, slot);
}
- onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError);
+ mWrapper.mHandlerThread.getThreadHandler().postDelayed(
+ () -> onSocketKeepaliveEvent(slot, mWrapper.mStartKeepaliveError),
+ mWrapper.mKeepaliveResponseDelay);
}
@Override
public void stopSocketKeepalive(Message msg) {
- onSocketKeepaliveEvent(msg.arg1, mWrapper.mStopKeepaliveError);
+ final int slot = msg.arg1;
+ mWrapper.mHandlerThread.getThreadHandler().postDelayed(
+ () -> onSocketKeepaliveEvent(slot, mWrapper.mStopKeepaliveError),
+ mWrapper.mKeepaliveResponseDelay);
}
@Override
@@ -248,6 +256,10 @@
mStopKeepaliveError = reason;
}
+ public void setKeepaliveResponseDelay(long delay) {
+ mKeepaliveResponseDelay = delay;
+ }
+
public void setExpectedKeepaliveSlot(Integer slot) {
mExpectedKeepaliveSlot = slot;
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1f23bf3..7dfac9c 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4292,6 +4292,32 @@
myNet = connectKeepaliveNetwork(lp);
mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
+ // Check that a stop followed by network disconnects does not result in crash.
+ try (SocketKeepalive ka = mCm.createSocketKeepalive(
+ myNet, testSocket, myIPv4, dstIPv4, executor, callback)) {
+ ka.start(validKaInterval);
+ callback.expectStarted();
+ // Delay the response of keepalive events in networkAgent long enough to make sure
+ // the follow-up network disconnection will be processed first.
+ mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS);
+ ka.stop();
+
+ // Make sure the stop has been processed. Wait for executor idle is needed to prevent
+ // flaky since the actual stop call to the service is delegated to executor thread.
+ waitForIdleSerialExecutor(executor, TIMEOUT_MS);
+ waitForIdle();
+
+ mWiFiNetworkAgent.disconnect();
+ mWiFiNetworkAgent.expectDisconnected();
+ callback.expectStopped();
+ callback.assertNoCallback();
+ }
+
+ // Reconnect.
+ waitForIdle();
+ myNet = connectKeepaliveNetwork(lp);
+ mWiFiNetworkAgent.setStartKeepaliveEvent(SocketKeepalive.SUCCESS);
+
// Check that keepalive slots start from 1 and increment. The first one gets slot 1.
mWiFiNetworkAgent.setExpectedKeepaliveSlot(1);
int srcPort2 = 0;
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 7c150f9..0af6266 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -115,13 +115,13 @@
}
case FILETYPE_INPUTDEVICECONFIGURATION: {
- PropertyMap* map;
- status_t status = PropertyMap::load(String8(filename), &map);
- if (status) {
- error("Error %d parsing input device configuration file.\n\n", status);
+ android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
+ PropertyMap::load(String8(filename));
+ if (!propertyMap.ok()) {
+ error("Error %d parsing input device configuration file.\n\n",
+ propertyMap.error().code());
return false;
}
- delete map;
break;
}
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index 3f5c673..1c297e7 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -798,9 +798,15 @@
method public int describeContents();
method public String getFqdn();
method public String getFriendlyName();
+ method @Nullable public long[] getMatchAllOis();
+ method @Nullable public long[] getMatchAnyOis();
+ method @Nullable public String[] getOtherHomePartners();
method public long[] getRoamingConsortiumOis();
method public void setFqdn(String);
method public void setFriendlyName(String);
+ method public void setMatchAllOis(@Nullable long[]);
+ method public void setMatchAnyOis(@Nullable long[]);
+ method public void setOtherHomePartners(@Nullable String[]);
method public void setRoamingConsortiumOis(long[]);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR;
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index aab4a2d..fd45ebe 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -508,7 +508,7 @@
field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
- field public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
+ field @Deprecated public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
field public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
field public static final String EXTRA_OSU_NETWORK = "android.net.wifi.extra.OSU_NETWORK";
field public static final String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state";
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c76f4a6..2219bfc 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1053,9 +1053,6 @@
/**
* Broadcast intent action indicating that the link configuration changed on wifi.
- * <br />Included Extras:
- * <br />{@link #EXTRA_LINK_PROPERTIES}: {@link android.net.LinkProperties} object associated
- * with the Wi-Fi network.
* <br /> No permissions are required to listen to this broadcast.
* @hide
*/
@@ -1071,8 +1068,12 @@
* Included in the {@link #ACTION_LINK_CONFIGURATION_CHANGED} broadcast.
*
* Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+ *
+ * @deprecated this extra is no longer populated.
+ *
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_LINK_PROPERTIES = "android.net.wifi.extra.LINK_PROPERTIES";
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
index 8f34579..35a8ff6 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
@@ -16,6 +16,7 @@
package android.net.wifi.hotspot2.pps;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -139,16 +140,26 @@
* (MO) tree for more detail.
*/
private long[] mMatchAllOis = null;
+
/**
- * @hide
+ * Set a list of HomeOIs such that all OIs in the list must match an OI in the Roaming
+ * Consortium advertised by a hotspot operator. The list set by this API will have precedence
+ * over {@link #setMatchAnyOis(long[])}, meaning the list set in {@link #setMatchAnyOis(long[])}
+ * will only be used for matching if the list set by this API is null or empty.
+ *
+ * @param matchAllOis An array of longs containing the HomeOIs
*/
- public void setMatchAllOis(long[] matchAllOis) {
+ public void setMatchAllOis(@Nullable long[] matchAllOis) {
mMatchAllOis = matchAllOis;
}
+
/**
- * @hide
+ * Get the list of HomeOIs such that all OIs in the list must match an OI in the Roaming
+ * Consortium advertised by a hotspot operator.
+ *
+ * @return An array of longs containing the HomeOIs
*/
- public long[] getMatchAllOis() {
+ public @Nullable long[] getMatchAllOis() {
return mMatchAllOis;
}
@@ -159,23 +170,34 @@
* of that Hotspot provider (e.g. successful authentication with such Hotspot
* is possible).
*
- * {@link #mMatchAllOIs} will have precedence over this one, meaning this list will
- * only be used for matching if {@link #mMatchAllOIs} is null or empty.
+ * The list set by {@link #setMatchAllOis(long[])} will have precedence over this one, meaning
+ * this list will only be used for matching if the list set by {@link #setMatchAllOis(long[])}
+ * is null or empty.
*
* Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object
* (MO) tree for more detail.
*/
private long[] mMatchAnyOis = null;
+
/**
- * @hide
+ * Set a list of HomeOIs such that any OI in the list matches an OI in the Roaming Consortium
+ * advertised by a hotspot operator. The list set by {@link #setMatchAllOis(long[])}
+ * will have precedence over this API, meaning this list will only be used for matching if the
+ * list set by {@link #setMatchAllOis(long[])} is null or empty.
+ *
+ * @param matchAnyOis An array of longs containing the HomeOIs
*/
- public void setMatchAnyOis(long[] matchAnyOis) {
+ public void setMatchAnyOis(@Nullable long[] matchAnyOis) {
mMatchAnyOis = matchAnyOis;
}
+
/**
- * @hide
+ * Get a list of HomeOIs such that any OI in the list matches an OI in the Roaming Consortium
+ * advertised by a hotspot operator.
+ *
+ * @return An array of longs containing the HomeOIs
*/
- public long[] getMatchAnyOis() {
+ public @Nullable long[] getMatchAnyOis() {
return mMatchAnyOis;
}
@@ -186,16 +208,25 @@
* operator merges between the providers.
*/
private String[] mOtherHomePartners = null;
+
/**
- * @hide
+ * Set the list of FQDN (Fully Qualified Domain Name) of other Home partner providers.
+ *
+ * @param otherHomePartners Array of Strings containing the FQDNs of other Home partner
+ * providers
*/
- public void setOtherHomePartners(String[] otherHomePartners) {
+ public void setOtherHomePartners(@Nullable String[] otherHomePartners) {
mOtherHomePartners = otherHomePartners;
}
+
/**
- * @hide
+ * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in
+ * the profile.
+ *
+ * @return Array of Strings containing the FQDNs of other Home partner providers set in the
+ * profile
*/
- public String[] getOtherHomePartners() {
+ public @Nullable String[] getOtherHomePartners() {
return mOtherHomePartners;
}