Merge "angle_gl_driver_all_angle should accept integer."
diff --git a/ApiDocs.bp b/ApiDocs.bp
index c82fee0f..faa0e5d 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -65,7 +65,7 @@
"test-base/src/**/*.java",
":opt-telephony-srcs",
":opt-net-voip-srcs",
- ":art-module-public-api-stubs-source",
+ ":art.module.public.api{.public.stubs.source}",
":conscrypt.module.public.api{.public.stubs.source}",
":android_icu4j_public_api_files",
"test-mock/src/**/*.java",
@@ -135,7 +135,7 @@
],
knowntags: [
"docs/knowntags.txt",
- ":known-oj-tags",
+ ":art.module.public.api{.doctags}",
],
custom_template: "droiddoc-templates-sdk",
resourcesdir: "docs/html/reference/images/",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 852fcc6..a3a2094 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -47,7 +47,7 @@
"core/java/**/*.logtags",
":opt-telephony-srcs",
":opt-net-voip-srcs",
- ":art-module-public-api-stubs-source",
+ ":art.module.public.api{.public.stubs.source}",
":android_icu4j_public_api_files",
"**/package.html",
],
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index a701f86..ecd1499 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -139,7 +139,6 @@
final IntSupplier mViewVisibility;
- int mSeq;
int mFrameNumber;
int mFlags;
@@ -156,7 +155,7 @@
void runBenchmark(BenchmarkState state) throws RemoteException {
final IWindowSession session = WindowManagerGlobal.getWindowSession();
while (state.keepRunning()) {
- session.relayout(mWindow, mSeq, mParams, mWidth, mHeight,
+ session.relayout(mWindow, mParams, mWidth, mHeight,
mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrames,
mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls,
mOutSurfaceSize, mOutBlastSurfaceControl);
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index c52b130..b11d7464 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -107,7 +107,7 @@
final InputChannel inputChannel = new InputChannel();
long startTime = SystemClock.elapsedRealtimeNanos();
- session.addToDisplay(this, mSeq, mLayoutParams, View.VISIBLE,
+ session.addToDisplay(this, mLayoutParams, View.VISIBLE,
Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets,
mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index bb9f13f..5cebf8d 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -128,7 +128,7 @@
*/
public static final String KEY_USE_REVOCABLE_FD_FOR_READS =
"use_revocable_fd_for_reads";
- public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = true;
+ public static final boolean DEFAULT_USE_REVOCABLE_FD_FOR_READS = false;
public static boolean USE_REVOCABLE_FD_FOR_READS =
DEFAULT_USE_REVOCABLE_FD_FOR_READS;
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
index e83ea3a..ced2fb5 100644
--- a/apex/media/OWNERS
+++ b/apex/media/OWNERS
@@ -1,7 +1,10 @@
andrewlewis@google.com
aquilescanta@google.com
chz@google.com
+hdmoon@google.com
hkuang@google.com
+jinpark@google.com
+klhyun@google.com
lnilsson@google.com
marcone@google.com
sungsoo@google.com
diff --git a/apex/media/aidl/Android.bp b/apex/media/aidl/Android.bp
index 409a048..c2b00d5 100644
--- a/apex/media/aidl/Android.bp
+++ b/apex/media/aidl/Android.bp
@@ -15,21 +15,21 @@
//
filegroup {
- name: "stable-mediasession2-aidl-srcs",
+ name: "stable-media-aidl-srcs",
srcs: ["stable/**/*.aidl"],
path: "stable",
}
filegroup {
- name: "private-mediasession2-aidl-srcs",
+ name: "private-media-aidl-srcs",
srcs: ["private/**/I*.aidl"],
path: "private",
}
filegroup {
- name: "mediasession2-aidl-srcs",
+ name: "media-aidl-srcs",
srcs: [
- ":private-mediasession2-aidl-srcs",
- ":stable-mediasession2-aidl-srcs",
+ ":private-media-aidl-srcs",
+ ":stable-media-aidl-srcs",
],
}
diff --git a/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl b/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl
new file mode 100644
index 0000000..92d673f
--- /dev/null
+++ b/apex/media/aidl/stable/android/media/MediaParceledListSlice.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable MediaParceledListSlice<T>;
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index ce4b030..813631e 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -54,9 +54,10 @@
filegroup {
name: "updatable-media-srcs",
srcs: [
+ ":media-aidl-srcs",
+ ":mediaparceledlistslice-java-srcs",
":mediaparser-srcs",
":mediasession2-java-srcs",
- ":mediasession2-aidl-srcs",
],
}
@@ -77,6 +78,15 @@
}
filegroup {
+ name: "mediaparceledlistslice-java-srcs",
+ srcs: [
+ "java/android/media/MediaParceledListSlice.java",
+ "java/android/media/BaseMediaParceledListSlice.java",
+ ],
+ path: "java",
+}
+
+filegroup {
name: "mediaparser-srcs",
srcs: [
"java/android/media/MediaParser.java"
diff --git a/apex/media/framework/api/module-lib-current.txt b/apex/media/framework/api/module-lib-current.txt
index d802177..d2826d0 100644
--- a/apex/media/framework/api/module-lib-current.txt
+++ b/apex/media/framework/api/module-lib-current.txt
@@ -1 +1,15 @@
// Signature format: 2.0
+package android.media {
+
+ public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
+ ctor public MediaParceledListSlice(@NonNull java.util.List<T>);
+ method public int describeContents();
+ method @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
+ method public java.util.List<T> getList();
+ method public void setInlineCountLimit(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
+ }
+
+}
+
diff --git a/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
new file mode 100644
index 0000000..fb66609
--- /dev/null
+++ b/apex/media/framework/java/android/media/BaseMediaParceledListSlice.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This is a copied version of BaseParceledListSlice in framework with hidden API usages
+ * removed.
+ *
+ * Transfer a large list of Parcelable objects across an IPC. Splits into
+ * multiple transactions if needed.
+ *
+ * Caveat: for efficiency and security, all elements must be the same concrete type.
+ * In order to avoid writing the class name of each object, we must ensure that
+ * each object is the same type, or else unparceling then reparceling the data may yield
+ * a different result if the class name encoded in the Parcelable is a Base type.
+ * See b/17671747.
+ *
+ * @hide
+ */
+abstract class BaseMediaParceledListSlice<T> implements Parcelable {
+ private static String TAG = "BaseMediaParceledListSlice";
+ private static boolean DEBUG = false;
+
+ /*
+ * TODO get this number from somewhere else. For now set it to a quarter of
+ * the 1MB limit.
+ */
+ // private static final int MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes();
+ private static final int MAX_IPC_SIZE = 64 * 1024;
+
+ private final List<T> mList;
+
+ private int mInlineCountLimit = Integer.MAX_VALUE;
+
+ public BaseMediaParceledListSlice(List<T> list) {
+ mList = list;
+ }
+
+ @SuppressWarnings("unchecked")
+ BaseMediaParceledListSlice(Parcel p, ClassLoader loader) {
+ final int N = p.readInt();
+ mList = new ArrayList<T>(N);
+ if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
+ if (N <= 0) {
+ return;
+ }
+
+ Parcelable.Creator<?> creator = readParcelableCreator(p, loader);
+ Class<?> listElementClass = null;
+
+ int i = 0;
+ while (i < N) {
+ if (p.readInt() == 0) {
+ break;
+ }
+
+ final T parcelable = readCreator(creator, p, loader);
+ if (listElementClass == null) {
+ listElementClass = parcelable.getClass();
+ } else {
+ verifySameType(listElementClass, parcelable.getClass());
+ }
+
+ mList.add(parcelable);
+
+ if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
+ i++;
+ }
+ if (i >= N) {
+ return;
+ }
+ final IBinder retriever = p.readStrongBinder();
+ while (i < N) {
+ if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInt(i);
+ try {
+ retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
+ return;
+ }
+ while (i < N && reply.readInt() != 0) {
+ final T parcelable = readCreator(creator, reply, loader);
+ verifySameType(listElementClass, parcelable.getClass());
+
+ mList.add(parcelable);
+
+ if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
+ i++;
+ }
+ reply.recycle();
+ data.recycle();
+ }
+ }
+
+ private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
+ if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+ Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+ (Parcelable.ClassLoaderCreator<?>) creator;
+ return (T) classLoaderCreator.createFromParcel(p, loader);
+ }
+ return (T) creator.createFromParcel(p);
+ }
+
+ private static void verifySameType(final Class<?> expected, final Class<?> actual) {
+ if (!actual.equals(expected)) {
+ throw new IllegalArgumentException("Can't unparcel type "
+ + (actual == null ? null : actual.getName()) + " in list of type "
+ + (expected == null ? null : expected.getName()));
+ }
+ }
+
+ public List<T> getList() {
+ return mList;
+ }
+
+ /**
+ * Set a limit on the maximum number of entries in the array that will be included
+ * inline in the initial parcelling of this object.
+ */
+ public void setInlineCountLimit(int maxCount) {
+ mInlineCountLimit = maxCount;
+ }
+
+ /**
+ * Write this to another Parcel. Note that this discards the internal Parcel
+ * and should not be used anymore. This is so we can pass this to a Binder
+ * where we won't have a chance to call recycle on this.
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ final int N = mList.size();
+ final int callFlags = flags;
+ dest.writeInt(N);
+ if (DEBUG) Log.d(TAG, "Writing " + N + " items");
+ if (N > 0) {
+ final Class<?> listElementClass = mList.get(0).getClass();
+ writeParcelableCreator(mList.get(0), dest);
+ int i = 0;
+ while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
+ dest.writeInt(1);
+
+ final T parcelable = mList.get(i);
+ verifySameType(listElementClass, parcelable.getClass());
+ writeElement(parcelable, dest, callFlags);
+
+ if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
+ i++;
+ }
+ if (i < N) {
+ dest.writeInt(0);
+ Binder retriever = new Binder() {
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ if (code != FIRST_CALL_TRANSACTION) {
+ return super.onTransact(code, data, reply, flags);
+ }
+ int i = data.readInt();
+ if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
+ while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
+ reply.writeInt(1);
+
+ final T parcelable = mList.get(i);
+ verifySameType(listElementClass, parcelable.getClass());
+ writeElement(parcelable, reply, callFlags);
+
+ if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
+ i++;
+ }
+ if (i < N) {
+ if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
+ reply.writeInt(0);
+ }
+ return true;
+ }
+ };
+ if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
+ dest.writeStrongBinder(retriever);
+ }
+ }
+ }
+
+ abstract void writeElement(T parcelable, Parcel reply, int callFlags);
+
+ abstract void writeParcelableCreator(T parcelable, Parcel dest);
+
+ abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
+}
diff --git a/apex/media/framework/java/android/media/MediaParceledListSlice.java b/apex/media/framework/java/android/media/MediaParceledListSlice.java
new file mode 100644
index 0000000..e1223f6
--- /dev/null
+++ b/apex/media/framework/java/android/media/MediaParceledListSlice.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This is a copied version of MediaParceledListSlice in framework with hidden API usages removed,
+ * and also with some lint error fixed.
+ *
+ * Transfer a large list of Parcelable objects across an IPC. Splits into
+ * multiple transactions if needed.
+ *
+ * @see BaseMediaParceledListSlice
+ *
+ * TODO: Remove this from @SystemApi once all the MediaSession related classes are moved
+ * to apex (or ParceledListSlice moved to apex). This class is temporaily added to system API
+ * for moving classes step by step.
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class MediaParceledListSlice<T extends Parcelable>
+ extends BaseMediaParceledListSlice<T> {
+ public MediaParceledListSlice(@NonNull List<T> list) {
+ super(list);
+ }
+
+ private MediaParceledListSlice(Parcel in, ClassLoader loader) {
+ super(in, loader);
+ }
+
+ @NonNull
+ public static <T extends Parcelable> MediaParceledListSlice<T> emptyList() {
+ return new MediaParceledListSlice<T>(Collections.<T> emptyList());
+ }
+
+ @Override
+ public int describeContents() {
+ int contents = 0;
+ final List<T> list = getList();
+ for (int i=0; i<list.size(); i++) {
+ contents |= list.get(i).describeContents();
+ }
+ return contents;
+ }
+
+ @Override
+ void writeElement(T parcelable, Parcel dest, int callFlags) {
+ parcelable.writeToParcel(dest, callFlags);
+ }
+
+ @Override
+ void writeParcelableCreator(T parcelable, Parcel dest) {
+ dest.writeParcelableCreator((Parcelable) parcelable);
+ }
+
+ @Override
+ Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
+ return from.readParcelableCreator(loader);
+ }
+
+ @NonNull
+ @SuppressWarnings("unchecked")
+ public static final Parcelable.ClassLoaderCreator<MediaParceledListSlice> CREATOR =
+ new Parcelable.ClassLoaderCreator<MediaParceledListSlice>() {
+ public MediaParceledListSlice createFromParcel(Parcel in) {
+ return new MediaParceledListSlice(in, null);
+ }
+
+ @Override
+ public MediaParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
+ return new MediaParceledListSlice(in, loader);
+ }
+
+ @Override
+ public MediaParceledListSlice[] newArray(int size) {
+ return new MediaParceledListSlice[size];
+ }
+ };
+}
diff --git a/api/current.txt b/api/current.txt
index 4b0901a..8776d2d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6138,6 +6138,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
+ field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
field public static final int FLAG_NO_CREATE = 536870912; // 0x20000000
field public static final int FLAG_ONE_SHOT = 1073741824; // 0x40000000
field public static final int FLAG_UPDATE_CURRENT = 134217728; // 0x8000000
@@ -8783,6 +8784,7 @@
method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
+ method public void onServiceChanged(@NonNull android.bluetooth.BluetoothGatt);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
}
@@ -16508,6 +16510,23 @@
package android.graphics.text {
+ public class GlyphStyle {
+ ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
+ ctor public GlyphStyle(@NonNull android.graphics.Paint);
+ method public void applyToPaint(@NonNull android.graphics.Paint);
+ method @ColorInt public int getColor();
+ method public int getFlags();
+ method @FloatRange(from=0) public float getFontSize();
+ method @FloatRange(from=0) public float getScaleX();
+ method @FloatRange(from=0) public float getSkewX();
+ method public void setColor(@ColorInt int);
+ method public void setFlags(int);
+ method public void setFontSize(@FloatRange(from=0) float);
+ method public void setFromPaint(@NonNull android.graphics.Paint);
+ method public void setScaleX(@FloatRange(from=0) float);
+ method public void setSkewX(@FloatRange(from=0) float);
+ }
+
public class LineBreaker {
method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16568,6 +16587,25 @@
method @NonNull public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
}
+ public final class PositionedGlyphs {
+ method public float getAscent();
+ method public float getDescent();
+ method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
+ method public float getOriginX();
+ method public float getOriginY();
+ method public float getPositionX(@IntRange(from=0) int);
+ method public float getPositionY(@IntRange(from=0) int);
+ method @NonNull public android.graphics.text.GlyphStyle getStyle();
+ method public float getTotalAdvance();
+ method @IntRange(from=0) public int glyphCount();
+ }
+
+ public class TextShaper {
+ method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
+ method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
+ }
+
}
package android.hardware {
@@ -26358,6 +26396,7 @@
method public boolean containsKey(String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(String);
+ method @IntRange(from=0) public int getBitmapDimensionLimit();
method @NonNull public android.media.MediaDescription getDescription();
method public long getLong(String);
method public android.media.Rating getRating(String);
@@ -26407,6 +26446,7 @@
method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
method public android.media.MediaMetadata.Builder putString(String, String);
method public android.media.MediaMetadata.Builder putText(String, CharSequence);
+ method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
}
@Deprecated public abstract class MediaMetadataEditor {
@@ -46096,6 +46136,7 @@
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
@@ -46795,6 +46836,7 @@
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+ field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
@@ -46989,6 +47031,10 @@
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+ field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
+ field public static final int USSD_OVER_CS_PREFERRED = 0; // 0x0
+ field public static final int USSD_OVER_IMS_ONLY = 3; // 0x3
+ field public static final int USSD_OVER_IMS_PREFERRED = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
@@ -47163,9 +47209,9 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoWcdma> CREATOR;
}
- public abstract class CellLocation {
- ctor public CellLocation();
- method public static android.telephony.CellLocation getEmpty();
+ @Deprecated public abstract class CellLocation {
+ ctor @Deprecated public CellLocation();
+ method @Deprecated public static android.telephony.CellLocation getEmpty();
method @Deprecated public static void requestLocationUpdate();
}
@@ -48683,19 +48729,19 @@
package android.telephony.cdma {
- public class CdmaCellLocation extends android.telephony.CellLocation {
- ctor public CdmaCellLocation();
- ctor public CdmaCellLocation(android.os.Bundle);
- method public static double convertQuartSecToDecDegrees(int);
- method public void fillInNotifierBundle(android.os.Bundle);
- method public int getBaseStationId();
- method public int getBaseStationLatitude();
- method public int getBaseStationLongitude();
- method public int getNetworkId();
- method public int getSystemId();
- method public void setCellLocationData(int, int, int);
- method public void setCellLocationData(int, int, int, int, int);
- method public void setStateInvalid();
+ @Deprecated public class CdmaCellLocation extends android.telephony.CellLocation {
+ ctor @Deprecated public CdmaCellLocation();
+ ctor @Deprecated public CdmaCellLocation(android.os.Bundle);
+ method @Deprecated public static double convertQuartSecToDecDegrees(int);
+ method @Deprecated public void fillInNotifierBundle(android.os.Bundle);
+ method @Deprecated public int getBaseStationId();
+ method @Deprecated public int getBaseStationLatitude();
+ method @Deprecated public int getBaseStationLongitude();
+ method @Deprecated public int getNetworkId();
+ method @Deprecated public int getSystemId();
+ method @Deprecated public void setCellLocationData(int, int, int);
+ method @Deprecated public void setCellLocationData(int, int, int, int, int);
+ method @Deprecated public void setStateInvalid();
}
}
@@ -48895,15 +48941,15 @@
package android.telephony.gsm {
- public class GsmCellLocation extends android.telephony.CellLocation {
- ctor public GsmCellLocation();
- ctor public GsmCellLocation(android.os.Bundle);
- method public void fillInNotifierBundle(android.os.Bundle);
- method public int getCid();
- method public int getLac();
- method public int getPsc();
- method public void setLacAndCid(int, int);
- method public void setStateInvalid();
+ @Deprecated public class GsmCellLocation extends android.telephony.CellLocation {
+ ctor @Deprecated public GsmCellLocation();
+ ctor @Deprecated public GsmCellLocation(android.os.Bundle);
+ method @Deprecated public void fillInNotifierBundle(android.os.Bundle);
+ method @Deprecated public int getCid();
+ method @Deprecated public int getLac();
+ method @Deprecated public int getPsc();
+ method @Deprecated public void setLacAndCid(int, int);
+ method @Deprecated public void setStateInvalid();
}
@Deprecated public final class SmsManager {
@@ -49990,6 +50036,10 @@
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
+ public class StyledTextShaper {
+ method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
+ }
+
public interface TextDirectionHeuristic {
method public boolean isRtl(char[], int, int);
method public boolean isRtl(CharSequence, int, int);
@@ -53642,6 +53692,33 @@
field public int toolType;
}
+ public interface OnReceiveContentCallback<T extends android.view.View> {
+ method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull T);
+ method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload);
+ }
+
+ public static final class OnReceiveContentCallback.Payload {
+ method @NonNull public android.content.ClipData getClip();
+ method @Nullable public android.os.Bundle getExtras();
+ method public int getFlags();
+ method @Nullable public android.net.Uri getLinkUri();
+ method public int getSource();
+ field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+ field public static final int SOURCE_AUTOFILL = 3; // 0x3
+ field public static final int SOURCE_CLIPBOARD = 0; // 0x0
+ field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
+ field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
+ field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
+ }
+
+ public static final class OnReceiveContentCallback.Payload.Builder {
+ ctor public OnReceiveContentCallback.Payload.Builder(@NonNull android.content.ClipData, int);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload build();
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setFlags(int);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setLinkUri(@Nullable android.net.Uri);
+ }
+
public abstract class OrientationEventListener {
ctor public OrientationEventListener(android.content.Context);
ctor public OrientationEventListener(android.content.Context, int);
@@ -54193,6 +54270,7 @@
method @IdRes public int getNextFocusRightId();
method @IdRes public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
+ method @Nullable public android.view.OnReceiveContentCallback<? extends android.view.View> getOnReceiveContentCallback();
method @ColorInt public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
method @ColorInt public int getOutlineSpotShadowColor();
@@ -54544,6 +54622,7 @@
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
+ method public void setOnReceiveContentCallback(@Nullable android.view.OnReceiveContentCallback<? extends android.view.View>);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
@@ -60806,17 +60885,6 @@
method public android.view.View newGroupView(android.content.Context, android.database.Cursor, boolean, android.view.ViewGroup);
}
- public interface RichContentReceiver<T extends android.view.View> {
- method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes();
- method public boolean onReceive(@NonNull T, @NonNull android.content.ClipData, int, int);
- field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
- field public static final int SOURCE_AUTOFILL = 3; // 0x3
- field public static final int SOURCE_CLIPBOARD = 0; // 0x0
- field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
- field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
- field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
- }
-
public class ScrollView extends android.widget.FrameLayout {
ctor public ScrollView(android.content.Context);
ctor public ScrollView(android.content.Context, android.util.AttributeSet);
@@ -61371,10 +61439,10 @@
method public int getMinWidth();
method public final android.text.method.MovementMethod getMovementMethod();
method public int getOffsetForPosition(float, float);
+ method @Nullable public android.view.OnReceiveContentCallback<android.widget.TextView> getOnReceiveContentCallback();
method public android.text.TextPaint getPaint();
method public int getPaintFlags();
method public String getPrivateImeOptions();
- method @NonNull public android.widget.RichContentReceiver<android.widget.TextView> getRichContentReceiver();
method public int getSelectionEnd();
method public int getSelectionStart();
method @ColorInt public int getShadowColor();
@@ -61502,7 +61570,6 @@
method public void setPaintFlags(int);
method public void setPrivateImeOptions(String);
method public void setRawInputType(int);
- method public void setRichContentReceiver(@NonNull android.widget.RichContentReceiver<android.widget.TextView>);
method public void setScroller(android.widget.Scroller);
method public void setSelectAllOnFocus(boolean);
method public void setShadowLayer(float, float, float, int);
@@ -61543,7 +61610,6 @@
method public void setWidth(int);
field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
- field @NonNull public static final android.widget.RichContentReceiver<android.widget.TextView> DEFAULT_RICH_CONTENT_RECEIVER;
}
public enum TextView.BufferType {
@@ -61560,6 +61626,12 @@
field @NonNull public static final android.os.Parcelable.Creator<android.widget.TextView.SavedState> CREATOR;
}
+ public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> {
+ ctor public TextViewOnReceiveContentCallback();
+ method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull android.widget.TextView);
+ method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload);
+ }
+
public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
method @Nullable public android.content.res.Resources.Theme getDropDownViewTheme();
method public void setDropDownViewTheme(@Nullable android.content.res.Resources.Theme);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index be8ea9c..4a7c121 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -16,6 +16,14 @@
}
+package android.app.role {
+
+ public final class RoleManager {
+ method @Nullable public String getDefaultSmsPackage(int);
+ }
+
+}
+
package android.content.rollback {
public class RollbackManagerFrameworkInitializer {
@@ -45,8 +53,14 @@
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
- public static final class MediaMetadata.Builder {
- ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
+ public final class MediaParceledListSlice<T extends android.os.Parcelable> implements android.os.Parcelable {
+ ctor public MediaParceledListSlice(@NonNull java.util.List<T>);
+ method public int describeContents();
+ method @NonNull public static <T extends android.os.Parcelable> android.media.MediaParceledListSlice<T> emptyList();
+ method public java.util.List<T> getList();
+ method public void setInlineCountLimit(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.ClassLoaderCreator<android.media.MediaParceledListSlice> CREATOR;
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 91be5bd..949a54a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7429,7 +7429,7 @@
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
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 @Deprecated 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";
field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
@@ -7437,7 +7437,7 @@
field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
- field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
+ field @Deprecated public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0
@@ -10441,10 +10441,6 @@
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
- public final class DisconnectCause implements android.os.Parcelable {
- field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
- }
-
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
diff --git a/api/test-current.txt b/api/test-current.txt
index 9383152..4c2aa5a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -83,6 +83,7 @@
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String);
method public long getTotalRam();
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int);
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
method public static boolean isHighEndGfx();
method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String);
method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
@@ -766,6 +767,7 @@
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @Nullable public String getDefaultSmsPackage(int);
method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -1022,6 +1024,7 @@
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public void holdLock(int);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String);
@@ -3405,6 +3408,8 @@
field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
field public static final String NOTIFICATION_BADGING = "notification_badging";
field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content";
+ field public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
+ field public static final String SELECTED_SPELL_CHECKER_SUBTYPE = "selected_spell_checker_subtype";
field public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option";
field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
@@ -5288,6 +5293,7 @@
}
public interface WindowManager extends android.view.ViewManager {
+ method @RequiresPermission("android.permission.INJECT_EVENTS") public default void holdLock(int);
method public default void setShouldShowIme(int, boolean);
method public default void setShouldShowSystemDecors(int, boolean);
method public default void setShouldShowWithInsecureKeyguard(int, boolean);
@@ -5537,6 +5543,18 @@
}
+package android.view.textservice {
+
+ public final class SpellCheckerSubtype implements android.os.Parcelable {
+ field public static final int SUBTYPE_ID_NONE = 0; // 0x0
+ }
+
+ public final class TextServicesManager {
+ method public boolean isSpellCheckerEnabled();
+ }
+
+}
+
package android.widget {
public abstract class AbsListView extends android.widget.AdapterView<android.widget.ListAdapter> implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener {
@@ -5580,6 +5598,11 @@
method public void disableClockTick();
}
+ @android.widget.RemoteViews.RemoteView public class TextView extends android.view.View implements android.view.ViewTreeObserver.OnPreDrawListener {
+ method public void onActivityResult(int, int, @Nullable android.content.Intent);
+ field public static final int PROCESS_TEXT_REQUEST_CODE = 100; // 0x64
+ }
+
public class TimePicker extends android.widget.FrameLayout {
method public android.view.View getAmView();
method public android.view.View getHourView();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 4ccc7e6..cb65482 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -499,7 +499,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10084
+ // Next: 10087
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -598,6 +598,7 @@
DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
GeneralExternalStorageAccessStats general_external_storage_access_stats =
10085 [(module) = "mediaprovider"];
+ IncomingSms incoming_sms = 10086 [(module) = "telephony"];
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3191,6 +3192,8 @@
optional int32 end_y = 7; // Y coordinate for ACTION_MOVE event.
optional int32 left_boundary = 8; // left edge width + left inset
optional int32 right_boundary = 9; // screen width - (right edge width + right inset)
+ optional float ml_model_score = 10; // The score between 0 and 1 which is the prediction output
+ // for the Back Gesture model.
enum WindowHorizontalLocation {
DEFAULT_LOCATION = 0;
@@ -10461,6 +10464,59 @@
}
/**
+ * Pulls information for a single incoming SMS.
+ *
+ * Each pull creates multiple atoms, one for each SMS. The sequence is randomized when pulled.
+ *
+ * Pulled from:
+ * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java
+ */
+message IncomingSms {
+ // Format of the SMS (3GPP or 3GPP2).
+ optional android.telephony.SmsFormatEnum sms_format = 1;
+
+ // Technology of the SMS (CS or IMS).
+ optional android.telephony.SmsTechEnum sms_tech = 2;
+
+ // Radio access technology (RAT) used for the SMS. It can be IWLAN in case of IMS.
+ optional android.telephony.NetworkTypeEnum rat = 3;
+
+ // Type the SMS.
+ optional android.telephony.SmsTypeEnum sms_type = 4;
+
+ // Number of total parts.
+ optional int32 total_parts = 5;
+
+ // Number of received parts (if smaller than total parts, the SMS was dropped).
+ optional int32 received_parts = 6;
+
+ // Indicates if the incoming SMS was blocked.
+ optional bool blocked = 7;
+
+ // Indicate a specific error handling the SMS
+ optional android.telephony.SmsIncomingErrorEnum error = 8;
+
+ // Whether the SMS was received while roaming.
+ optional bool is_roaming = 9;
+
+ // Index of the SIM is used, 0 for single-SIM devices.
+ optional int32 sim_slot_index = 10;
+
+ // Whether the device was in multi-SIM mode (with multiple active SIM profiles).
+ optional bool is_multi_sim = 11;
+
+ // Whether the message was received with an eSIM profile.
+ optional bool is_esim = 12;
+
+ // Carrier ID of the SIM card used for the SMS.
+ // See https://source.android.com/devices/tech/config/carrierid.
+ optional int32 carrier_id = 13;
+
+ // Random message ID.
+ optional int64 message_id = 14;
+}
+
+/**
* Logs gnss stats from location service provider
*
* Pulled from:
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index cd9c4e5..03b178a 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -76,7 +76,7 @@
ABinderProcess_startThreadPool();
std::shared_ptr<LogEventQueue> eventQueue =
- std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
+ std::make_shared<LogEventQueue>(4000 /*buffer limit. Buffer is NOT pre-allocated*/);
// Create the service
gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
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 2ae5763..12d3b94 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -501,6 +501,21 @@
const set<int64_t>& replacedStates,
vector<UpdateStatus>& metricsToUpdate) {
int metricIndex = 0;
+ for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
+ const CountMetric& metric = config.count_metric(i);
+ set<int64_t> conditionDependencies;
+ if (metric.has_condition()) {
+ conditionDependencies.insert(metric.condition());
+ }
+ if (!determineMetricUpdateStatus(
+ config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()},
+ conditionDependencies, metric.slice_by_state(), metric.links(),
+ oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ replacedMatchers, replacedConditions, replacedStates,
+ metricsToUpdate[metricIndex])) {
+ return false;
+ }
+ }
for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
const EventMetric& metric = config.event_metric(i);
set<int64_t> conditionDependencies;
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 65e5875..3b346c1 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
@@ -1094,6 +1094,162 @@
EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
}
+TEST_F(ConfigUpdateTest, TestCountMetricPreserve) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->add_slice_by_state(sliceState.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ 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, TestCountMetricDefinitionChange) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ // Change bucket size, which should change the proto, causing replacement.
+ metric->set_bucket(TEN_MINUTES);
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ 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, TestCountMetricWhatChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ 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, TestCountMetricConditionChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ Predicate predicate = CreateScreenIsOnPredicate();
+ *config.add_predicate() = predicate;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->set_condition(predicate.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ 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, TestCountMetricStateChanged) {
+ StatsdConfig config;
+ AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+ *config.add_atom_matcher() = startMatcher;
+ AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+ *config.add_atom_matcher() = stopMatcher;
+ AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+ *config.add_atom_matcher() = whatMatcher;
+
+ State sliceState = CreateScreenState();
+ *config.add_state() = sliceState;
+
+ CountMetric* metric = config.add_count_metric();
+ metric->set_id(12345);
+ metric->set_what(whatMatcher.id());
+ metric->add_slice_by_state(sliceState.id());
+ metric->set_bucket(ONE_HOUR);
+
+ // Create an initial config.
+ EXPECT_TRUE(initConfig(config));
+
+ unordered_map<int64_t, int> metricToActivationMap;
+ vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+ EXPECT_TRUE(determineAllMetricUpdateStatuses(
+ config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+ /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+ /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+ EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
StatsdConfig config;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 70ca49d..e75d2f6 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4796,4 +4796,19 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * This is intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ public void holdLock(int durationMs) {
+ try {
+ getService().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 95bbebe..1a4db4e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -696,4 +696,10 @@
* @param enable set it to true to enable the app freezer, false to disable it.
*/
boolean enableAppFreezer(in boolean enable);
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * This is intended for use by the tests that need to imitate lock contention.
+ */
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index cd352e1..e8937a8 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -19,12 +19,16 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -35,6 +39,7 @@
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.ArraySet;
+import android.util.Log;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.IResultReceiver;
@@ -102,11 +107,20 @@
* FLAG_ONE_SHOT, <b>both</b> FLAG_ONE_SHOT and FLAG_NO_CREATE need to be supplied.
*/
public final class PendingIntent implements Parcelable {
+ private static final String TAG = "PendingIntent";
private final IIntentSender mTarget;
private IResultReceiver mCancelReceiver;
private IBinder mWhitelistToken;
private ArraySet<CancelListener> mCancelListeners;
+ /**
+ * It is now required to specify either {@link #FLAG_IMMUTABLE}
+ * or {@link #FLAG_MUTABLE} when creating a PendingIntent.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.R)
+ static final long PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED = 160794467L;
+
/** @hide */
@IntDef(flag = true,
value = {
@@ -115,6 +129,7 @@
FLAG_CANCEL_CURRENT,
FLAG_UPDATE_CURRENT,
FLAG_IMMUTABLE,
+ FLAG_MUTABLE,
Intent.FILL_IN_ACTION,
Intent.FILL_IN_DATA,
@@ -175,6 +190,21 @@
public static final int FLAG_IMMUTABLE = 1<<26;
/**
+ * Flag indicating that the created PendingIntent should be mutable.
+ * This flag cannot be combined with {@link #FLAG_IMMUTABLE}. <p>Up until
+ * {@link android.os.Build.VERSION_CODES#R}, PendingIntents are assumed to
+ * be mutable by default, unless {@link #FLAG_IMMUTABLE} is set. Starting
+ * with {@link android.os.Build.VERSION_CODES#S}, it will be required to
+ * explicitly specify the mutability of PendingIntents on creation with
+ * either (@link #FLAG_IMMUTABLE} or {@link #FLAG_MUTABLE}. It is strongly
+ * recommended to use {@link #FLAG_IMMUTABLE} when creating a
+ * PendingIntent. {@link #FLAG_MUTABLE} should only be used when some
+ * functionality relies on modifying the underlying intent, e.g. any
+ * PendingIntent that needs to be used with inline reply or bubbles.
+ */
+ public static final int FLAG_MUTABLE = 1<<25;
+
+ /**
* Exception thrown when trying to send through a PendingIntent that
* has been canceled or is otherwise no longer able to execute the request.
*/
@@ -286,6 +316,27 @@
sOnMarshaledListener.set(listener);
}
+ private static void checkFlags(int flags, String packageName) {
+ final boolean flagImmutableSet = (flags & PendingIntent.FLAG_IMMUTABLE) != 0;
+ final boolean flagMutableSet = (flags & PendingIntent.FLAG_MUTABLE) != 0;
+ String msg = packageName + ": Targeting S+ (version " + Build.VERSION_CODES.S
+ + " and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE"
+ + " be specified when creating a PendingIntent.\nStrongly consider"
+ + " using FLAG_IMMUTABLE, only use FLAG_MUTABLE if some functionality"
+ + " depends on the PendingIntent being mutable, e.g. if it needs to"
+ + " be used with inline replies or bubbles.";
+
+ if (flagImmutableSet && flagMutableSet) {
+ throw new IllegalArgumentException(
+ "Cannot set both FLAG_IMMUTABLE and FLAG_MUTABLE for PendingIntent");
+ }
+
+ if (Compatibility.isChangeEnabled(PENDING_INTENT_EXPLICIT_MUTABILITY_REQUIRED)
+ && !flagImmutableSet && !flagMutableSet) {
+ Log.e(TAG, msg);
+ }
+ }
+
/**
* Retrieve a PendingIntent that will start a new activity, like calling
* {@link Context#startActivity(Intent) Context.startActivity(Intent)}.
@@ -350,6 +401,7 @@
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
@@ -376,6 +428,7 @@
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.migrateExtraStreamToClipData(context);
intent.prepareToLeaveProcess(context);
@@ -495,6 +548,7 @@
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
+ checkFlags(flags, packageName);
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -521,6 +575,7 @@
intents[i].prepareToLeaveProcess(context);
resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
}
+ checkFlags(flags, packageName);
try {
IIntentSender target =
ActivityManager.getService().getIntentSenderWithFeature(
@@ -572,6 +627,7 @@
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
@@ -651,6 +707,7 @@
String packageName = context.getPackageName();
String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
context.getContentResolver()) : null;
+ checkFlags(flags, packageName);
try {
intent.prepareToLeaveProcess(context);
IIntentSender target =
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
index 61dac0d..c547ef1 100644
--- a/core/java/android/app/people/IPeopleManager.aidl
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -39,4 +39,10 @@
/** Removes all the recent conversations and uncaches their cached shortcuts. */
void removeAllRecentConversations();
+
+ /**
+ * Returns the last interaction with the specified conversation. If the
+ * conversation can't be found or no interactions have been recorded, returns 0L.
+ */
+ long getLastInteraction(in String packageName, int userId, in String shortcutId);
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 253c73796..26edba3 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -634,6 +634,8 @@
* @hide
*/
@Nullable
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
public String getDefaultSmsPackage(@UserIdInt int userId) {
try {
return mService.getDefaultSmsPackage(userId);
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index 9f6b828..1c40cff 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.NonNull;
+
/**
* This abstract class is used to implement {@link BluetoothGatt} callbacks.
*/
@@ -203,8 +205,7 @@
* called to re-discover the services.
*
* @param gatt GATT client involved
- * @hide
*/
- public void onServiceChanged(BluetoothGatt gatt) {
+ public void onServiceChanged(@NonNull BluetoothGatt gatt) {
}
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 24282365..ba894ae 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -795,4 +795,6 @@
boolean isAutoRevokeWhitelisted(String packageName);
void grantImplicitAccess(int queryingUid, String visibleAuthority);
+
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 4eec56c..79e23b3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8305,4 +8305,19 @@
public static void uncorkPackageInfoCache() {
PropertyInvalidatedCache.uncorkInvalidations(PermissionManager.CACHE_KEY_PACKAGE_INFO);
}
+
+ /**
+ * Holds the PM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ public void holdLock(int durationMs) {
+ try {
+ ActivityThread.getPackageManager().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 1061121..0d0bfb3 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2175,10 +2175,8 @@
/**
* <p>The desired zoom ratio</p>
- * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} with dual purposes of crop and zoom, the
- * application can now choose to use this tag to specify the desired zoom level. The
- * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
- * crop to achieve aspect ratios different than the native camera sensor.</p>
+ * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} for zoom, the application can now choose to
+ * use this tag to specify the desired zoom level.</p>
* <p>By using this control, the application gains a simpler way to control zoom, which can
* be a combination of optical and digital zoom. For example, a multi-camera system may
* contain more than one lens with different focal lengths, and the user can use optical
@@ -2860,11 +2858,18 @@
* respectively.</p>
* <p>The camera device may adjust the crop region to account for rounding and other hardware
* requirements; the final crop region used will be included in the output capture result.</p>
+ * <p>The camera sensor output aspect ratio depends on factors such as output stream
+ * combination and {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}, and shouldn't be adjusted by using
+ * this control. And the camera device will treat different camera sensor output sizes
+ * (potentially with in-sensor crop) as the same crop of
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}. As a result, the application shouldn't assume the
+ * maximum crop region always maps to the same aspect ratio or field of view for the
+ * sensor output.</p>
* <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
* to take advantage of better support for zoom with logical multi-camera. The benefits
* include better precision with optical-digital zoom combination, and ability to do
* zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
- * the capture request must be either letterboxing or pillarboxing (but not both). The
+ * the capture request should be left as the default activeArray size. The
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
@@ -2874,6 +2879,7 @@
* capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
* @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 29a53fb..8cfa086 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2405,10 +2405,8 @@
/**
* <p>The desired zoom ratio</p>
- * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} with dual purposes of crop and zoom, the
- * application can now choose to use this tag to specify the desired zoom level. The
- * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} can still be used to specify the horizontal or vertical
- * crop to achieve aspect ratios different than the native camera sensor.</p>
+ * <p>Instead of using {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} for zoom, the application can now choose to
+ * use this tag to specify the desired zoom level.</p>
* <p>By using this control, the application gains a simpler way to control zoom, which can
* be a combination of optical and digital zoom. For example, a multi-camera system may
* contain more than one lens with different focal lengths, and the user can use optical
@@ -3506,11 +3504,18 @@
* respectively.</p>
* <p>The camera device may adjust the crop region to account for rounding and other hardware
* requirements; the final crop region used will be included in the output capture result.</p>
+ * <p>The camera sensor output aspect ratio depends on factors such as output stream
+ * combination and {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}, and shouldn't be adjusted by using
+ * this control. And the camera device will treat different camera sensor output sizes
+ * (potentially with in-sensor crop) as the same crop of
+ * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}. As a result, the application shouldn't assume the
+ * maximum crop region always maps to the same aspect ratio or field of view for the
+ * sensor output.</p>
* <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
* to take advantage of better support for zoom with logical multi-camera. The benefits
* include better precision with optical-digital zoom combination, and ability to do
* zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
- * the capture request must be either letterboxing or pillarboxing (but not both). The
+ * the capture request should be left as the default activeArray size. The
* coordinate system is post-zoom, meaning that the activeArraySize or
* preCorrectionActiveArraySize covers the camera device's field of view "after" zoom. See
* {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
@@ -3520,6 +3525,7 @@
* capability and mode</p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
* @see CaptureRequest#CONTROL_ZOOM_RATIO
* @see CaptureRequest#DISTORTION_CORRECTION_MODE
* @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index c12bb39..997efbe 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -746,7 +746,7 @@
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
if (mService == null) {
Slog.w(TAG, "setUdfpsOverlayController: no fingerprint service");
return;
@@ -763,14 +763,14 @@
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void onFingerDown(int x, int y, float minor, float major) {
+ public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
if (mService == null) {
Slog.w(TAG, "onFingerDown: no fingerprint service");
return;
}
try {
- mService.onFingerDown(x, y, minor, major);
+ mService.onFingerDown(sensorId, x, y, minor, major);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
@@ -780,14 +780,14 @@
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
- public void onFingerUp() {
+ public void onFingerUp(int sensorId) {
if (mService == null) {
Slog.w(TAG, "onFingerDown: no fingerprint service");
return;
}
try {
- mService.onFingerUp();
+ mService.onFingerUp(sensorId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
index 718141a..d26346c 100644
--- a/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
+++ b/core/java/android/hardware/fingerprint/FingerprintSensorProperties.java
@@ -18,9 +18,7 @@
import android.annotation.IntDef;
import android.hardware.biometrics.SensorProperties;
-import android.hardware.face.FaceSensorProperties;
import android.os.Parcel;
-import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index cc2b520..68013ea 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -121,10 +121,10 @@
void initializeConfiguration(int sensorId, int strength);
// Notifies about a finger touching the sensor area.
- void onFingerDown(int x, int y, float minor, float major);
+ void onFingerDown(int sensorId, int x, int y, float minor, float major);
// Notifies about a finger leaving the sensor area.
- void onFingerUp();
+ void onFingerUp(int sensorId);
// Sets the controller for managing the UDFPS overlay.
void setUdfpsOverlayController(in IUdfpsOverlayController controller);
diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index a57726c..58b7046 100644
--- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -21,11 +21,11 @@
*/
oneway interface IUdfpsOverlayController {
// Shows the overlay.
- void showUdfpsOverlay();
+ void showUdfpsOverlay(int sensorId);
// Hides the overlay.
- void hideUdfpsOverlay();
+ void hideUdfpsOverlay(int sensorId);
// Shows debug messages on the UDFPS overlay.
- void setDebugMessage(String message);
+ void setDebugMessage(int sensorId, String message);
}
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index f6cd726..1173c31 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -78,4 +78,26 @@
*/
public abstract boolean transferTouchFocus(@NonNull IBinder fromChannelToken,
@NonNull IBinder toChannelToken);
+
+ /** Registers the {@link LidSwitchCallback} to begin receiving notifications. */
+ public abstract void registerLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
+
+ /**
+ * Unregisters a {@link LidSwitchCallback callback} previously registered with
+ * {@link #registerLidSwitchCallback(LidSwitchCallback)}.
+ */
+ public abstract void unregisterLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
+
+ /** Callback interface for notifications relating to the lid switch. */
+ public interface LidSwitchCallback {
+ /**
+ * This callback is invoked when the lid switch changes state. Will be triggered once on
+ * registration of the callback with a {@code whenNanos} of 0 and then on every subsequent
+ * change in lid switch state.
+ *
+ * @param whenNanos the time when the change occurred
+ * @param lidOpen true if the lid is open
+ */
+ void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
+ }
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 651494d..cad103e9 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -21,13 +21,14 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.LinkPropertiesUtils;
-import android.net.util.LinkPropertiesUtils.CompareResult;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.net.module.util.LinkPropertiesUtils;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0eb3c1e..51c5a50 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -20,12 +20,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.MacAddressUtils;
import android.net.wifi.WifiInfo;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.Preconditions;
+import com.android.net.module.util.MacAddressUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 9876076..a8b45e9 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -22,11 +22,12 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.NetUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.net.module.util.NetUtils;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.Inet4Address;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fdbf79a..d889b155 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -21,6 +21,7 @@
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.job.JobParameters;
import android.compat.annotation.UnsupportedAppUsage;
@@ -2889,14 +2890,17 @@
/**
* Returns the approximate CPU time (in microseconds) spent by the system server handling
- * incoming service calls from apps.
+ * incoming service calls from apps. The result is returned as an array of longs,
+ * organized as a sequence like this:
+ * <pre>
+ * cluster1-speeed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...
+ * </pre>
*
- * @param cluster the index of the CPU cluster.
- * @param step the index of the CPU speed. This is not the actual speed of the CPU.
* @see com.android.internal.os.PowerProfile#getNumCpuClusters()
* @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
*/
- public abstract long getSystemServiceTimeAtCpuSpeed(int cluster, int step);
+ @Nullable
+ public abstract long[] getSystemServiceTimeAtCpuSpeeds();
/**
* Returns the total, last, or current battery uptime in microseconds.
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 52ee04c..54f80c6 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -267,6 +267,7 @@
}
if (appInfo == null) {
Log.w(TAG, "Debug layer app '" + packageName + "' not installed");
+ return "";
}
final String abi = chooseAbi(appInfo);
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index a92d91b..e996809 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -98,6 +98,8 @@
boolean isAmbientDisplaySuppressedForToken(String token);
// returns whether ambient display is suppressed by any app with any token.
boolean isAmbientDisplaySuppressed();
+ // returns whether ambient display is suppressed by the given app with the given token.
+ boolean isAmbientDisplaySuppressedForTokenByApp(String token, int appUid);
// Forces the system to suspend even if there are held wakelocks.
boolean forceSuspend();
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 3265829..50f0c28 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2191,6 +2191,27 @@
}
/**
+ * Returns true if ambient display is suppressed by the given {@code appUid} with the given
+ * {@code token}.
+ *
+ * <p>This method will return false if {@link #isAmbientDisplayAvailable()} is false.
+ *
+ * @param token The identifier of the ambient display suppression.
+ * @param appUid The uid of the app that suppressed ambient display.
+ * @hide
+ */
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.READ_DREAM_STATE,
+ android.Manifest.permission.READ_DREAM_SUPPRESSION })
+ public boolean isAmbientDisplaySuppressedForTokenByApp(@NonNull String token, int appUid) {
+ try {
+ return mService.isAmbientDisplaySuppressedForTokenByApp(token, appUid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the reason the phone was last shutdown. Calling app must have the
* {@link android.Manifest.permission#DEVICE_POWER} permission to request this information.
* @return Reason for shutdown as an int, {@link #SHUTDOWN_REASON_UNKNOWN} if the file could
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 07867e2..e978608 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7663,6 +7663,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
/**
@@ -7673,6 +7675,8 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
+ @SuppressLint("NoSettingsProvider")
public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
"selected_spell_checker_subtype";
@@ -14464,6 +14468,15 @@
*/
public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE =
"nr_nsa_tracking_screen_off_mode";
+
+ /**
+ * Whether to show People Space.
+ * Values are:
+ * 0: Disabled (default)
+ * 1: Enabled
+ * @hide
+ */
+ public static final String SHOW_PEOPLE_SPACE = "show_people_space";
}
/**
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 19860eb..0f46ffc 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -879,7 +879,7 @@
com.android.internal.R.style.Animation_Wallpaper;
InputChannel inputChannel = new InputChannel();
- if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
+ if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
mDisplay.getDisplayId(), mWinFrames.frame, mWinFrames.contentInsets,
mWinFrames.stableInsets, mWinFrames.displayCutout, inputChannel,
mInsetsState, mTempControls) < 0) {
@@ -903,7 +903,7 @@
}
final int relayoutResult = mSession.relayout(
- mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
+ mWindow, mLayout, mWidth, mHeight,
View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl,
mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl);
if (mSurfaceControl.isValid()) {
diff --git a/core/java/android/text/StyledTextShaper.java b/core/java/android/text/StyledTextShaper.java
new file mode 100644
index 0000000..bf90614
--- /dev/null
+++ b/core/java/android/text/StyledTextShaper.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.text.PositionedGlyphs;
+import android.graphics.text.TextShaper;
+
+import java.util.List;
+
+/**
+ * Provides text shaping for multi-styled text.
+ *
+ * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint)
+ */
+public class StyledTextShaper {
+ private StyledTextShaper() {}
+
+
+ /**
+ * Shape multi-styled text.
+ *
+ * @param text a styled text.
+ * @param start a start index of shaping target in the text.
+ * @param count a length of shaping target in the text.
+ * @param dir a text direction.
+ * @param paint a paint
+ * @return a shape result.
+ */
+ public static @NonNull List<PositionedGlyphs> shapeText(
+ @NonNull CharSequence text, int start, int count,
+ @NonNull TextDirectionHeuristic dir, @NonNull TextPaint paint) {
+ MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
+ text, start, start + count, dir, null);
+ TextLine tl = TextLine.obtain();
+ try {
+ tl.set(paint, text, start, start + count,
+ mp.getParagraphDir(),
+ mp.getDirections(start, start + count),
+ false /* tabstop is not supported */,
+ null,
+ -1, -1 // ellipsis is not supported.
+ );
+ return tl.shape();
+ } finally {
+ TextLine.recycle(tl);
+ }
+ }
+
+}
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 3c51fa7..b826832 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -23,6 +23,8 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
+import android.graphics.text.PositionedGlyphs;
+import android.graphics.text.TextShaper;
import android.os.Build;
import android.text.Layout.Directions;
import android.text.Layout.TabStops;
@@ -35,6 +37,7 @@
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
+import java.util.List;
/**
* Represents a line of styled text, for measuring in visual order and
@@ -307,6 +310,36 @@
}
/**
+ * Shape the TextLine.
+ */
+ List<PositionedGlyphs> shape() {
+ List<PositionedGlyphs> glyphs = new ArrayList<>();
+ float horizontal = 0;
+ float x = 0;
+ final int runCount = mDirections.getRunCount();
+ for (int runIndex = 0; runIndex < runCount; runIndex++) {
+ final int runStart = mDirections.getRunStart(runIndex);
+ if (runStart > mLen) break;
+ final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
+ final boolean runIsRtl = mDirections.isRunRtl(runIndex);
+
+ int segStart = runStart;
+ for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
+ if (j == runLimit || charAt(j) == TAB_CHAR) {
+ horizontal += shapeRun(glyphs, segStart, j, runIsRtl, x + horizontal,
+ runIndex != (runCount - 1) || j != mLen);
+
+ if (j != runLimit) { // charAt(j) == TAB_CHAR
+ horizontal = mDir * nextTab(horizontal * mDir);
+ }
+ segStart = j + 1;
+ }
+ }
+ }
+ return glyphs;
+ }
+
+ /**
* Returns the signed graphical offset from the leading margin.
*
* Following examples are all for measuring offset=3. LX(e.g. L0, L1, ...) denotes a
@@ -483,12 +516,12 @@
if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
float w = -measureRun(start, limit, limit, runIsRtl, null);
- handleRun(start, limit, limit, runIsRtl, c, x + w, top,
+ handleRun(start, limit, limit, runIsRtl, c, null, x + w, top,
y, bottom, null, false);
return w;
}
- return handleRun(start, limit, limit, runIsRtl, c, x, top,
+ return handleRun(start, limit, limit, runIsRtl, c, null, x, top,
y, bottom, null, needWidth);
}
@@ -507,10 +540,35 @@
*/
private float measureRun(int start, int offset, int limit, boolean runIsRtl,
FontMetricsInt fmi) {
- return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true);
+ return handleRun(start, offset, limit, runIsRtl, null, null, 0, 0, 0, 0, fmi, true);
}
/**
+ * Shape a unidirectional (but possibly multi-styled) run of text.
+ *
+ * @param glyphs the output positioned glyphs list
+ * @param start the line-relative start
+ * @param limit the line-relative limit
+ * @param runIsRtl true if the run is right-to-left
+ * @param x the position of the run that is closest to the leading margin
+ * @param needWidth true if the width value is required.
+ * @return the signed width of the run, based on the paragraph direction.
+ * Only valid if needWidth is true.
+ */
+ private float shapeRun(List<PositionedGlyphs> glyphs, int start,
+ int limit, boolean runIsRtl, float x, boolean needWidth) {
+
+ if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
+ float w = -measureRun(start, limit, limit, runIsRtl, null);
+ handleRun(start, limit, limit, runIsRtl, null, glyphs, x + w, 0, 0, 0, null, false);
+ return w;
+ }
+
+ return handleRun(start, limit, limit, runIsRtl, null, glyphs, x, 0, 0, 0, null, needWidth);
+ }
+
+
+ /**
* Walk the cursor through this line, skipping conjuncts and
* zero-width characters.
*
@@ -841,6 +899,7 @@
* @param end the end of the text
* @param runIsRtl true if the run is right-to-left
* @param c the canvas, can be null if rendering is not needed
+ * @param glyphs the output positioned glyph list, can be null if not necessary
* @param x the edge of the run closest to the leading margin
* @param top the top of the line
* @param y the baseline
@@ -854,7 +913,7 @@
*/
private float handleText(TextPaint wp, int start, int end,
int contextStart, int contextEnd, boolean runIsRtl,
- Canvas c, float x, int top, int y, int bottom,
+ Canvas c, List<PositionedGlyphs> glyphs, float x, int top, int y, int bottom,
FontMetricsInt fmi, boolean needWidth, int offset,
@Nullable ArrayList<DecorationInfo> decorations) {
@@ -878,16 +937,20 @@
totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
}
- if (c != null) {
- final float leftX, rightX;
- if (runIsRtl) {
- leftX = x - totalWidth;
- rightX = x;
- } else {
- leftX = x;
- rightX = x + totalWidth;
- }
+ final float leftX, rightX;
+ if (runIsRtl) {
+ leftX = x - totalWidth;
+ rightX = x;
+ } else {
+ leftX = x;
+ rightX = x + totalWidth;
+ }
+ if (glyphs != null) {
+ shapeTextRun(glyphs, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
+ }
+
+ if (c != null) {
if (wp.bgColor != 0) {
int previousColor = wp.getColor();
Paint.Style previousStyle = wp.getStyle();
@@ -1072,6 +1135,7 @@
* @param limit the limit of the run
* @param runIsRtl true if the run is right-to-left
* @param c the canvas, can be null
+ * @param glyphs the output positioned glyphs, can be null
* @param x the end of the run closest to the leading margin
* @param top the top of the line
* @param y the baseline
@@ -1082,7 +1146,8 @@
* valid if needWidth is true
*/
private float handleRun(int start, int measureLimit,
- int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
+ int limit, boolean runIsRtl, Canvas c,
+ List<PositionedGlyphs> glyphs, float x, int top, int y,
int bottom, FontMetricsInt fmi, boolean needWidth) {
if (measureLimit < start || measureLimit > limit) {
@@ -1115,7 +1180,7 @@
wp.set(mPaint);
wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit()));
wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
- return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top,
+ return handleText(wp, start, limit, start, limit, runIsRtl, c, glyphs, x, top,
y, bottom, fmi, needWidth, measureLimit, null);
}
@@ -1196,8 +1261,8 @@
adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
activePaint.setEndHyphenEdit(
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
- x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
- top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
+ x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c,
+ glyphs, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations);
activeStart = j;
@@ -1223,7 +1288,7 @@
adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
activePaint.setEndHyphenEdit(
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
- x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
+ x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, glyphs, x,
top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations);
}
@@ -1260,6 +1325,45 @@
}
/**
+ * Shape a text run with the set-up paint.
+ *
+ * @param glyphs the output positioned glyphs list
+ * @param paint the paint used to render the text
+ * @param start the start of the run
+ * @param end the end of the run
+ * @param contextStart the start of context for the run
+ * @param contextEnd the end of the context for the run
+ * @param runIsRtl true if the run is right-to-left
+ * @param x the x position of the left edge of the run
+ */
+ private void shapeTextRun(List<PositionedGlyphs> glyphs, TextPaint paint,
+ int start, int end, int contextStart, int contextEnd, boolean runIsRtl, float x) {
+
+ int count = end - start;
+ int contextCount = contextEnd - contextStart;
+ if (mCharsValid) {
+ glyphs.add(TextShaper.shapeTextRun(
+ mChars,
+ start, count,
+ contextStart, contextCount,
+ x, 0f,
+ runIsRtl,
+ paint
+ ));
+ } else {
+ glyphs.add(TextShaper.shapeTextRun(
+ mText,
+ mStart + start, count,
+ mStart + contextStart, contextCount,
+ x, 0f,
+ runIsRtl,
+ paint
+ ));
+ }
+ }
+
+
+ /**
* Returns the next tab position.
*
* @param h the (unsigned) offset from the leading margin
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 2ded473..7a117f1 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -227,7 +227,7 @@
* @param level The level to check.
* @return Whether or not that this is allowed to be logged.
* @throws IllegalArgumentException is thrown if the tag.length() > 23
- * for Nougat (7.0) releases (API <= 23) and prior, there is no
+ * for Nougat (7.0) and prior releases (API <= 25), there is no
* tag limit of concern after this API level.
*/
@FastNative
diff --git a/core/java/android/util/imetracing/ImeTracing.java b/core/java/android/util/imetracing/ImeTracing.java
new file mode 100644
index 0000000..865d560
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracing.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imetracing;
+
+import android.app.ActivityThread;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.view.IInputMethodManager;
+
+/**
+ *
+ * An abstract class that declares the methods for ime trace related operations - enable trace,
+ * schedule trace and add new trace to buffer. Both the client and server side classes can use
+ * it by getting an implementation through {@link ImeTracing#getInstance()}.
+ *
+ * @hide
+ */
+public abstract class ImeTracing {
+
+ static final String TAG = "imeTracing";
+ public static final String PROTO_ARG = "--proto-com-android-imetracing";
+
+ private static ImeTracing sInstance;
+ static boolean sEnabled = false;
+ IInputMethodManager mService;
+
+ ImeTracing() throws ServiceNotFoundException {
+ mService = IInputMethodManager.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE));
+ }
+
+ /**
+ * Returns an instance of {@link ImeTracingServerImpl} when called from a server side class
+ * and an instance of {@link ImeTracingClientImpl} when called from a client side class.
+ * Useful to schedule a dump for next frame or save a dump when certain methods are called.
+ *
+ * @return Instance of one of the children classes of {@link ImeTracing}
+ */
+ public static ImeTracing getInstance() {
+ if (sInstance == null) {
+ try {
+ sInstance = isSystemProcess()
+ ? new ImeTracingServerImpl() : new ImeTracingClientImpl();
+ } catch (RemoteException | ServiceNotFoundException e) {
+ Log.e(TAG, "Exception while creating ImeTracing instance", e);
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Sends request to start proto dump to {@link ImeTracingServerImpl} when called from a
+ * server process and to {@link ImeTracingClientImpl} when called from a client process.
+ */
+ public abstract void triggerDump();
+
+ /**
+ * @param proto dump to be added to the buffer
+ */
+ public abstract void addToBuffer(ProtoOutputStream proto);
+
+ /**
+ * @param shell The shell command to process
+ * @return {@code 0} if the command was successfully processed, {@code -1} otherwise
+ */
+ public abstract int onShellCommand(ShellCommand shell);
+
+ /**
+ * Sets whether ime tracing is enabled.
+ *
+ * @param enabled Tells whether ime tracing should be enabled or disabled.
+ */
+ public void setEnabled(boolean enabled) {
+ sEnabled = enabled;
+ }
+
+ /**
+ * @return {@code true} if dumping is enabled, {@code false} otherwise.
+ */
+ public boolean isEnabled() {
+ return sEnabled;
+ }
+
+ /**
+ * @return {@code true} if tracing is available, {@code false} otherwise.
+ */
+ public boolean isAvailable() {
+ return mService != null;
+ }
+
+ private static boolean isSystemProcess() {
+ return ActivityThread.isSystem();
+ }
+}
diff --git a/core/java/android/util/imetracing/ImeTracingClientImpl.java b/core/java/android/util/imetracing/ImeTracingClientImpl.java
new file mode 100644
index 0000000..e5d7d338
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracingClientImpl.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util.imetracing;
+
+import android.os.RemoteException;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+import android.view.inputmethod.InputMethodManager;
+
+/**
+ * @hide
+ */
+class ImeTracingClientImpl extends ImeTracing {
+
+ private boolean mDumpInProgress;
+ private final Object mDumpInProgressLock = new Object();
+
+ ImeTracingClientImpl() throws ServiceNotFoundException, RemoteException {
+ sEnabled = mService.isImeTraceEnabled();
+ }
+
+ @Override
+ public void addToBuffer(ProtoOutputStream proto) {
+ }
+
+ @Override
+ public int onShellCommand(ShellCommand shell) {
+ return -1;
+ }
+
+ @Override
+ public void triggerDump() {
+ if (isAvailable() && isEnabled()) {
+ boolean doDump = false;
+ synchronized (mDumpInProgressLock) {
+ if (!mDumpInProgress) {
+ mDumpInProgress = true;
+ doDump = true;
+ }
+ }
+
+ if (doDump) {
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ InputMethodManager.dumpProto(proto);
+ mService.startProtoDump(proto.getBytes());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while sending ime-related client dump to server", e);
+ } finally {
+ mDumpInProgress = false;
+ }
+ }
+ }
+ }
+}
diff --git a/core/java/android/util/imetracing/ImeTracingServerImpl.java b/core/java/android/util/imetracing/ImeTracingServerImpl.java
new file mode 100644
index 0000000..350cf57
--- /dev/null
+++ b/core/java/android/util/imetracing/ImeTracingServerImpl.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 android.util.imetracing;
+
+import static android.os.Build.IS_USER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER_H;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.MAGIC_NUMBER_L;
+
+import android.os.RemoteException;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.ShellCommand;
+import android.util.Log;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.TraceBuffer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @hide
+ */
+class ImeTracingServerImpl extends ImeTracing {
+ private static final String TRACE_FILENAME = "/data/misc/wmtrace/ime_trace.pb";
+ private static final int BUFFER_CAPACITY = 4096 * 1024;
+
+ // Needed for winscope to auto-detect the dump type. Explained further in
+ // core.proto.android.view.inputmethod.inputmethodeditortrace.proto
+ private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+
+ private final TraceBuffer mBuffer;
+ private final File mTraceFile;
+ private final Object mEnabledLock = new Object();
+
+ ImeTracingServerImpl() throws ServiceNotFoundException {
+ mBuffer = new TraceBuffer<>(BUFFER_CAPACITY);
+ mTraceFile = new File(TRACE_FILENAME);
+ }
+
+ /**
+ * The provided dump is added to the current dump buffer {@link ImeTracingServerImpl#mBuffer}.
+ *
+ * @param proto dump to be added to the buffer
+ */
+ @Override
+ public void addToBuffer(ProtoOutputStream proto) {
+ if (isAvailable() && isEnabled()) {
+ mBuffer.add(proto);
+ }
+ }
+
+ /**
+ * Responds to a shell command of the format "adb shell cmd input_method ime tracing <command>"
+ *
+ * @param shell The shell command to process
+ * @return {@code 0} if the command was valid and successfully processed, {@code -1} otherwise
+ */
+ @Override
+ public int onShellCommand(ShellCommand shell) {
+ PrintWriter pw = shell.getOutPrintWriter();
+ String cmd = shell.getNextArgRequired();
+ switch (cmd) {
+ case "start":
+ startTrace(pw);
+ return 0;
+ case "stop":
+ stopTrace(pw);
+ return 0;
+ default:
+ pw.println("Unknown command: " + cmd);
+ pw.println("Input method trace options:");
+ pw.println(" start: Start tracing");
+ pw.println(" stop: Stop tracing");
+ return -1;
+ }
+ }
+
+ @Override
+ public void triggerDump() {
+ if (isAvailable() && isEnabled()) {
+ try {
+ mService.startProtoDump(null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while triggering proto dump", e);
+ }
+ }
+ }
+
+ private void writeTraceToFileLocked() {
+ try {
+ ProtoOutputStream proto = new ProtoOutputStream();
+ proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+ mBuffer.writeTraceToFile(mTraceFile, proto);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to write buffer to file", e);
+ }
+ }
+
+ @GuardedBy("mEnabledLock")
+ private void startTrace(PrintWriter pw) {
+ if (IS_USER) {
+ Log.w(TAG, "Warn: Tracing is not supported on user builds.");
+ return;
+ }
+
+ synchronized (mEnabledLock) {
+ if (isAvailable() && isEnabled()) {
+ Log.w(TAG, "Warn: Tracing is already started.");
+ return;
+ }
+
+ pw.println("Starting tracing to " + mTraceFile + ".");
+ sEnabled = true;
+ mBuffer.resetBuffer();
+ }
+ }
+
+ @GuardedBy("mEnabledLock")
+ private void stopTrace(PrintWriter pw) {
+ if (IS_USER) {
+ Log.w(TAG, "Warn: Tracing is not supported on user builds.");
+ return;
+ }
+
+ synchronized (mEnabledLock) {
+ if (!isAvailable() || !isEnabled()) {
+ Log.w(TAG, "Warn: Tracing is not available or not started.");
+ return;
+ }
+
+ pw.println("Stopping tracing and writing traces to " + mTraceFile + ".");
+ sEnabled = false;
+ writeTraceToFileLocked();
+ mBuffer.resetBuffer();
+ }
+ }
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 94e641c..193e674 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -120,12 +120,6 @@
void updatePointerIcon(float x, float y);
/**
- * System chrome visibility changes
- */
- void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
- int localValue, int localChanges);
-
- /**
* Called for non-application windows when the enter animation has completed.
*/
void dispatchWindowShown();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 8e875d7..daab70a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -378,11 +378,6 @@
boolean requestAssistScreenshot(IAssistDataReceiver receiver);
/**
- * Called by the status bar to notify Views of changes to System UI visiblity.
- */
- oneway void statusBarVisibilityChanged(int displayId, int visibility);
-
- /**
* Called by System UI to notify Window Manager to hide transient bars.
*/
oneway void hideTransientBars(int displayId);
@@ -757,4 +752,10 @@
*/
void requestScrollCapture(int displayId, IBinder behindClient, int taskId,
IScrollCaptureController controller);
+
+ /**
+ * Holds the WM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ */
+ void holdLock(in int durationMs);
}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 18c87c0..69a5faf 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -44,17 +44,17 @@
* {@hide}
*/
interface IWindowSession {
- int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
+ int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outFrame,
out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
- int addToDisplayAsUser(IWindow window, int seq, in WindowManager.LayoutParams attrs,
+ int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, in int userId,
out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets,
out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
out InsetsState insetsState, out InsetsSourceControl[] activeControls);
- int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
+ int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
in int viewVisibility, in int layerStackId, out Rect outContentInsets,
out Rect outStableInsets, out InsetsState insetsState);
@UnsupportedAppUsage
@@ -68,7 +68,6 @@
* to draw the window's contents.
*
* @param window The window being modified.
- * @param seq Ordering sequence number.
* @param attrs If non-null, new attributes to apply to the window.
* @param requestedWidth The width the window wants to be.
* @param requestedHeight The height the window wants to be.
@@ -106,7 +105,7 @@
* @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
* {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}.
*/
- int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
+ int relayout(IWindow window, in WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility,
int flags, long frameNumber, out ClientWindowFrames outFrames,
out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index 92772c1..efc0bd2 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -16,16 +16,23 @@
package android.view;
+import static android.view.ImeFocusControllerProto.HAS_IME_FOCUS;
+import static android.view.ImeFocusControllerProto.NEXT_SERVED_VIEW;
+import static android.view.ImeFocusControllerProto.SERVED_VIEW;
+
import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.UiThread;
import android.util.Log;
+import android.util.proto.ProtoOutputStream;
import android.view.inputmethod.InputMethodManager;
import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
+import java.util.Objects;
+
/**
* Responsible for IME focus handling inside {@link ViewRootImpl}.
* @hide
@@ -280,4 +287,12 @@
boolean hasImeFocus() {
return mHasImeFocus;
}
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(HAS_IME_FOCUS, mHasImeFocus);
+ proto.write(SERVED_VIEW, Objects.toString(mServedView));
+ proto.write(NEXT_SERVED_VIEW, Objects.toString(mNextServedView));
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 82f6036..dd1a194 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -16,6 +16,9 @@
package android.view;
+import static android.view.ImeInsetsSourceConsumerProto.FOCUSED_EDITOR;
+import static android.view.ImeInsetsSourceConsumerProto.INSETS_SOURCE_CONSUMER;
+import static android.view.ImeInsetsSourceConsumerProto.IS_REQUESTED_VISIBLE_AWAITING_CONTROL;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsState.ITYPE_IME;
@@ -24,6 +27,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl.Transaction;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
@@ -111,7 +115,6 @@
public @ShowResult int requestShow(boolean fromIme) {
// TODO: ResultReceiver for IME.
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
-
if (getControl() == null) {
// If control is null, schedule to show IME when control is available.
mIsRequestedVisibleAwaitingControl = true;
@@ -227,6 +230,17 @@
return Arrays.equals(parcel1.createByteArray(), parcel2.createByteArray());
}
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ super.dumpDebug(proto, INSETS_SOURCE_CONSUMER);
+ if (mFocusedEditor != null) {
+ mFocusedEditor.dumpDebug(proto, FOCUSED_EDITOR);
+ }
+ proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingControl);
+ proto.end(token);
+ }
+
private InputMethodManager getImm() {
return mController.getHost().getInputMethodManager();
}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6ffd892..71899fa 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -17,6 +17,14 @@
package android.view;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.InsetsAnimationControlImplProto.CURRENT_ALPHA;
+import static android.view.InsetsAnimationControlImplProto.IS_CANCELLED;
+import static android.view.InsetsAnimationControlImplProto.IS_FINISHED;
+import static android.view.InsetsAnimationControlImplProto.PENDING_ALPHA;
+import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION;
+import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS;
+import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH;
+import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX;
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsController.DEBUG;
@@ -38,6 +46,8 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -48,6 +58,7 @@
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Implements {@link WindowInsetsAnimationController}
@@ -122,6 +133,10 @@
mAnimationType = animationType;
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets));
+
+ if ((mTypes & WindowInsets.Type.ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
}
private boolean calculatePerceptible(Insets currentInsets, float currentAlpha) {
@@ -285,6 +300,20 @@
return mAnimation;
}
+ @Override
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(IS_CANCELLED, mCancelled);
+ proto.write(IS_FINISHED, mFinished);
+ proto.write(TMP_MATRIX, Objects.toString(mTmpMatrix));
+ proto.write(PENDING_INSETS, Objects.toString(mPendingInsets));
+ proto.write(PENDING_FRACTION, mPendingFraction);
+ proto.write(SHOWN_ON_FINISH, mShownOnFinish);
+ proto.write(CURRENT_ALPHA, mCurrentAlpha);
+ proto.write(PENDING_ALPHA, mPendingAlpha);
+ proto.end(token);
+ }
+
WindowInsetsAnimationControlListener getListener() {
return mListener;
}
diff --git a/core/java/android/view/InsetsAnimationControlRunner.java b/core/java/android/view/InsetsAnimationControlRunner.java
index 0711c3e..0275b52 100644
--- a/core/java/android/view/InsetsAnimationControlRunner.java
+++ b/core/java/android/view/InsetsAnimationControlRunner.java
@@ -16,6 +16,7 @@
package android.view;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsController.AnimationType;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type.InsetsType;
@@ -53,4 +54,14 @@
* @return The animation type this runner is running.
*/
@AnimationType int getAnimationType();
+
+ /**
+ *
+ * Export the state of classes that implement this interface into a protocol buffer
+ * output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of the implementation class
+ */
+ void dumpDebug(ProtoOutputStream proto, long fieldId);
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index 1236044..cc3cd27 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -25,6 +25,7 @@
import android.os.Trace;
import android.util.Log;
import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsController.AnimationType;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
@@ -122,6 +123,12 @@
@Override
@UiThread
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ mControl.dumpDebug(proto, fieldId);
+ }
+
+ @Override
+ @UiThread
public int getTypes() {
return mControl.getTypes();
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 92eade3..652781a 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsControllerProto.CONTROL;
+import static android.view.InsetsControllerProto.STATE;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.toInternalType;
@@ -41,6 +43,8 @@
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
@@ -298,6 +302,10 @@
@Override
public void onReady(WindowInsetsAnimationController controller, int types) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
+
mController = controller;
if (DEBUG) Log.d(TAG, "default animation onReady types: " + types);
@@ -812,6 +820,9 @@
@VisibleForTesting
public void show(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
// Handle pending request ready in case there was one set.
if (fromIme && mPendingImeControlRequest != null) {
PendingControlRequest pendingRequest = mPendingImeControlRequest;
@@ -860,6 +871,9 @@
}
void hide(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
@@ -894,6 +908,9 @@
listener.onCancelled(null);
return;
}
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
interpolator, animationType, getLayoutInsetsDuringAnimationMode(types),
@@ -1292,6 +1309,9 @@
private void hideDirectly(
@InsetsType int types, boolean animationFinished, @AnimationType int animationType) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType);
@@ -1299,6 +1319,9 @@
}
private void showDirectly(@InsetsType int types) {
+ if ((types & ime()) != 0) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
@@ -1318,6 +1341,16 @@
mState.dump(prefix + " ", pw);
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mState.dumpDebug(proto, STATE);
+ for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
+ InsetsAnimationControlRunner runner = mRunningAnimations.get(i).runner;
+ runner.dumpDebug(proto, CONTROL);
+ }
+ proto.end(token);
+ }
+
@VisibleForTesting
@Override
public void startAnimation(InsetsAnimationControlImpl controller,
@@ -1388,7 +1421,7 @@
}
@Override
- public @Appearance int getSystemBarsBehavior() {
+ public @Behavior int getSystemBarsBehavior() {
return mHost.getSystemBarsBehavior();
}
diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java
index 385b0bf..5a64a5d 100644
--- a/core/java/android/view/InsetsFlags.java
+++ b/core/java/android/view/InsetsFlags.java
@@ -16,13 +16,6 @@
package android.view;
-import static android.view.View.NAVIGATION_BAR_TRANSLUCENT;
-import static android.view.View.NAVIGATION_BAR_TRANSPARENT;
-import static android.view.View.STATUS_BAR_TRANSLUCENT;
-import static android.view.View.STATUS_BAR_TRANSPARENT;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -76,44 +69,4 @@
name = "SHOW_TRANSIENT_BARS_BY_SWIPE")
})
public @Behavior int behavior;
-
- /**
- * Converts system UI visibility to appearance.
- *
- * @param systemUiVisibility the system UI visibility to be converted.
- * @return the outcome {@link Appearance}
- */
- public static @Appearance int getAppearance(int systemUiVisibility) {
- int appearance = 0;
- appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LOW_PROFILE,
- APPEARANCE_LOW_PROFILE_BARS);
- appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
- APPEARANCE_LIGHT_STATUS_BARS);
- appearance |= convertFlag(systemUiVisibility, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
- APPEARANCE_LIGHT_NAVIGATION_BARS);
- appearance |= convertNoFlag(systemUiVisibility,
- STATUS_BAR_TRANSLUCENT | STATUS_BAR_TRANSPARENT, APPEARANCE_OPAQUE_STATUS_BARS);
- appearance |= convertNoFlag(systemUiVisibility,
- NAVIGATION_BAR_TRANSLUCENT | NAVIGATION_BAR_TRANSPARENT,
- APPEARANCE_OPAQUE_NAVIGATION_BARS);
- return appearance;
- }
-
- /**
- * Converts the system UI visibility into an appearance flag if the given visibility contains
- * the given system UI flag.
- */
- private static @Appearance int convertFlag(int systemUiVisibility, int systemUiFlag,
- @Appearance int appearance) {
- return (systemUiVisibility & systemUiFlag) != 0 ? appearance : 0;
- }
-
- /**
- * Converts the system UI visibility into an appearance flag if the given visibility doesn't
- * contains the given system UI flag.
- */
- private static @Appearance int convertNoFlag(int systemUiVisibility, int systemUiFlag,
- @Appearance int appearance) {
- return (systemUiVisibility & systemUiFlag) == 0 ? appearance : 0;
- }
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index dbf7570..41cc8459 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -16,6 +16,10 @@
package android.view;
+import static android.view.InsetsSourceProto.FRAME;
+import static android.view.InsetsSourceProto.TYPE;
+import static android.view.InsetsSourceProto.VISIBLE;
+import static android.view.InsetsSourceProto.VISIBLE_FRAME;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -25,6 +29,7 @@
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
@@ -183,6 +188,17 @@
return false;
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(TYPE, InsetsState.typeToString(mType));
+ mFrame.dumpDebug(proto, FRAME);
+ if (mVisibleFrame != null) {
+ mVisibleFrame.dumpDebug(proto, VISIBLE_FRAME);
+ }
+ proto.write(VISIBLE, mVisible);
+ proto.end(token);
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index ba40459..d7ceaf7 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -19,6 +19,13 @@
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
import static android.view.InsetsController.AnimationType;
import static android.view.InsetsController.DEBUG;
+import static android.view.InsetsSourceConsumerProto.HAS_WINDOW_FOCUS;
+import static android.view.InsetsSourceConsumerProto.INTERNAL_INSETS_TYPE;
+import static android.view.InsetsSourceConsumerProto.IS_REQUESTED_VISIBLE;
+import static android.view.InsetsSourceConsumerProto.PENDING_FRAME;
+import static android.view.InsetsSourceConsumerProto.PENDING_VISIBLE_FRAME;
+import static android.view.InsetsSourceConsumerProto.SOURCE_CONTROL;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.getDefaultVisibility;
import static android.view.InsetsState.toPublicType;
@@ -28,6 +35,8 @@
import android.annotation.Nullable;
import android.graphics.Rect;
import android.util.Log;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type.InsetsType;
@@ -319,6 +328,9 @@
@VisibleForTesting(visibility = PACKAGE)
public boolean notifyAnimationFinished() {
+ if (mType == ITYPE_IME) {
+ ImeTracing.getInstance().triggerDump();
+ }
if (mPendingFrame != null) {
InsetsSource source = mState.getSource(mType);
source.setFrame(mPendingFrame);
@@ -360,4 +372,21 @@
t.apply();
onPerceptible(mRequestedVisible);
}
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(INTERNAL_INSETS_TYPE, InsetsState.typeToString(mType));
+ proto.write(HAS_WINDOW_FOCUS, mHasWindowFocus);
+ proto.write(IS_REQUESTED_VISIBLE, mRequestedVisible);
+ if (mSourceControl != null) {
+ mSourceControl.dumpDebug(proto, SOURCE_CONTROL);
+ }
+ if (mPendingFrame != null) {
+ mPendingFrame.dumpDebug(proto, PENDING_FRAME);
+ }
+ if (mPendingVisibleFrame != null) {
+ mPendingVisibleFrame.dumpDebug(proto, PENDING_VISIBLE_FRAME);
+ }
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 51b4921..b45bd38 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -16,10 +16,17 @@
package android.view;
+import static android.graphics.PointProto.X;
+import static android.graphics.PointProto.Y;
+import static android.view.InsetsSourceControlProto.LEASH;
+import static android.view.InsetsSourceControlProto.POSITION;
+import static android.view.InsetsSourceControlProto.TYPE;
+
import android.annotation.Nullable;
import android.graphics.Point;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
@@ -120,4 +127,19 @@
return new InsetsSourceControl[size];
}
};
+
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(TYPE, InsetsState.typeToString(mType));
+
+ final long surfaceToken = proto.start(POSITION);
+ proto.write(X, mSurfacePosition.x);
+ proto.write(Y, mSurfacePosition.y);
+ proto.end(surfaceToken);
+
+ if (mLeash != null) {
+ mLeash.dumpDebug(proto, LEASH);
+ }
+ proto.end(token);
+ }
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index c5d0a10..eabb718 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.InsetsStateProto.DISPLAY_FRAME;
+import static android.view.InsetsStateProto.SOURCES;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
@@ -41,6 +43,7 @@
import android.os.Parcelable;
import android.util.ArraySet;
import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
@@ -545,6 +548,16 @@
}
}
+ void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ InsetsSource source = mSources[ITYPE_IME];
+ if (source != null) {
+ source.dumpDebug(proto, SOURCES);
+ }
+ mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME);
+ proto.end(token);
+ }
+
public static String typeToString(@InternalInsetsType int type) {
switch (type) {
case ITYPE_STATUS_BAR:
diff --git a/core/java/android/view/OnReceiveContentCallback.java b/core/java/android/view/OnReceiveContentCallback.java
new file mode 100644
index 0000000..73bcb93
--- /dev/null
+++ b/core/java/android/view/OnReceiveContentCallback.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Bundle;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Callback for apps to implement handling for insertion of content. "Content" here refers to both
+ * text and non-text (plain/styled text, HTML, images, videos, audio files, etc).
+ *
+ * <p>This callback can be attached to different types of UI components using
+ * {@link View#setOnReceiveContentCallback}.
+ *
+ * <p>For editable {@link android.widget.TextView} components, implementations can extend from
+ * {@link android.widget.TextViewOnReceiveContentCallback} to reuse default platform behavior for
+ * handling text.
+ *
+ * <p>Example implementation:<br>
+ * <pre class="prettyprint">
+ * public class MyOnReceiveContentCallback implements OnReceiveContentCallback<TextView> {
+ *
+ * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet(
+ * Set.of("image/*", "video/*"));
+ *
+ * @NonNull
+ * @Override
+ * public Set<String> getSupportedMimeTypes() {
+ * return SUPPORTED_MIME_TYPES;
+ * }
+ *
+ * @Override
+ * public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
+ * // ... app-specific logic to handle the content in the payload ...
+ * }
+ * }
+ * </pre>
+ *
+ * @param <T> The type of {@link View} with which this receiver can be associated.
+ */
+public interface OnReceiveContentCallback<T extends View> {
+ /**
+ * Receive the given content.
+ *
+ * <p>This function will only be invoked if the MIME type of the content is in the set of
+ * types returned by {@link #getSupportedMimeTypes}.
+ *
+ * <p>For text, if the view has a selection, the selection should be overwritten by the clip; if
+ * there's no selection, this method should insert the content at the current cursor position.
+ *
+ * <p>For non-text content (e.g. an image), the content may be inserted inline, or it may be
+ * added as an attachment (could potentially be shown in a completely separate view).
+ *
+ * @param view The view where the content insertion was requested.
+ * @param payload The content to insert and related metadata.
+ *
+ * @return Returns true if some or all of the content is accepted for insertion. If accepted,
+ * actual insertion may be handled asynchronously in the background and may or may not result in
+ * successful insertion. For example, the app may not end up inserting an accepted item if it
+ * exceeds the app's size limit for that type of content.
+ */
+ boolean onReceiveContent(@NonNull T view, @NonNull Payload payload);
+
+ /**
+ * Returns the MIME types that can be handled by this callback.
+ *
+ * <p>The {@link #onReceiveContent} callback method will only be invoked if the MIME type of the
+ * content is in the set of supported types returned here.
+ *
+ * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the
+ * keyboard, etc) may use this function to conditionally alter their behavior. For example, the
+ * keyboard may choose to hide its UI for inserting GIFs if the input field that has focus has
+ * a {@link OnReceiveContentCallback} set and the MIME types returned from this function don't
+ * include "image/gif".
+ *
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike formal RFC
+ * MIME types. As a result, you should always write your MIME types with lower case letters, or
+ * use {@link android.content.Intent#normalizeMimeType} to ensure that it is converted to lower
+ * case.</em>
+ *
+ * @param view The target view.
+ * @return An immutable set with the MIME types supported by this callback. The returned MIME
+ * types may contain wildcards such as "text/*", "image/*", etc.
+ */
+ @SuppressLint("CallbackMethodName")
+ @NonNull
+ Set<String> getSupportedMimeTypes(@NonNull T view);
+
+ /**
+ * Returns true if at least one of the MIME types of the given clip is
+ * {@link #getSupportedMimeTypes supported} by this receiver.
+ *
+ * @hide
+ */
+ default boolean supports(@NonNull T view, @NonNull ClipDescription description) {
+ for (String supportedMimeType : getSupportedMimeTypes(view)) {
+ if (description.hasMimeType(supportedMimeType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Holds all the relevant data for a request to {@link OnReceiveContentCallback}.
+ */
+ final class Payload {
+
+ /**
+ * Specifies the UI through which content is being inserted.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
+ SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Source {}
+
+ /**
+ * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
+ * "Paste as plain text" action in the insertion/selection menu).
+ */
+ public static final int SOURCE_CLIPBOARD = 0;
+
+ /**
+ * Specifies that the operation was triggered from the soft keyboard (also known as input
+ * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
+ * for more info.
+ */
+ public static final int SOURCE_INPUT_METHOD = 1;
+
+ /**
+ * Specifies that the operation was triggered by the drag/drop framework. See
+ * https://developer.android.com/guide/topics/ui/drag-drop for more info.
+ */
+ public static final int SOURCE_DRAG_AND_DROP = 2;
+
+ /**
+ * Specifies that the operation was triggered by the autofill framework. See
+ * https://developer.android.com/guide/topics/text/autofill for more info.
+ */
+ public static final int SOURCE_AUTOFILL = 3;
+
+ /**
+ * Specifies that the operation was triggered by a result from a
+ * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection
+ * menu.
+ */
+ public static final int SOURCE_PROCESS_TEXT = 4;
+
+ /**
+ * Returns the symbolic name of the given source.
+ *
+ * @hide
+ */
+ static String sourceToString(@Source int source) {
+ switch (source) {
+ case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
+ case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
+ case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
+ case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL";
+ case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT";
+ }
+ return String.valueOf(source);
+ }
+
+ /**
+ * Flags to configure the insertion behavior.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Flags {}
+
+ /**
+ * Flag requesting that the content should be converted to plain text prior to inserting.
+ */
+ public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
+
+ /**
+ * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
+ *
+ * @hide
+ */
+ static String flagsToString(@Flags int flags) {
+ if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
+ return "FLAG_CONVERT_TO_PLAIN_TEXT";
+ }
+ return String.valueOf(flags);
+ }
+
+ /**
+ * The data to be inserted.
+ */
+ @NonNull private final ClipData mClip;
+
+ /**
+ * The source of the operation. See {@code SOURCE_} constants.
+ */
+ private final @Source int mSource;
+
+ /**
+ * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
+ */
+ private final @Flags int mFlags;
+
+ /**
+ * Optional http/https URI for the content that may be provided by the IME. This is only
+ * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
+ * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
+ * IME.
+ */
+ @Nullable
+ private final Uri mLinkUri;
+
+ /**
+ * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
+ * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
+ * the IME.
+ */
+ @Nullable
+ private final Bundle mExtras;
+
+ private Payload(Builder b) {
+ this.mClip = Objects.requireNonNull(b.mClip);
+ this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT,
+ "source");
+ this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
+ this.mLinkUri = b.mLinkUri;
+ this.mExtras = b.mExtras;
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "Payload{"
+ + "clip=" + mClip.getDescription()
+ + ", source=" + sourceToString(mSource)
+ + ", flags=" + flagsToString(mFlags)
+ + ", linkUri=" + mLinkUri
+ + ", extras=" + mExtras
+ + "}";
+ }
+
+ /**
+ * The data to be inserted.
+ */
+ public @NonNull ClipData getClip() {
+ return mClip;
+ }
+
+ /**
+ * The source of the operation. See {@code SOURCE_} constants.
+ */
+ public @Source int getSource() {
+ return mSource;
+ }
+
+ /**
+ * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
+ */
+ public @Flags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Optional http/https URI for the content that may be provided by the IME. This is only
+ * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
+ * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
+ * IME.
+ */
+ public @Nullable Uri getLinkUri() {
+ return mLinkUri;
+ }
+
+ /**
+ * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
+ * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
+ * the IME.
+ */
+ public @Nullable Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Builder for {@link Payload}.
+ */
+ public static final class Builder {
+ @NonNull private final ClipData mClip;
+ private final @Source int mSource;
+ private @Flags int mFlags;
+ @Nullable private Uri mLinkUri;
+ @Nullable private Bundle mExtras;
+
+ /**
+ * Creates a new builder.
+ * @param clip The data to insert.
+ * @param source The source of the operation. See {@code SOURCE_} constants.
+ */
+ public Builder(@NonNull ClipData clip, @Source int source) {
+ mClip = clip;
+ mSource = source;
+ }
+
+ /**
+ * Sets flags that control content insertion behavior.
+ * @param flags Optional flags to configure the insertion behavior. Use 0 for default
+ * behavior. See {@code FLAG_} constants.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setFlags(@Flags int flags) {
+ mFlags = flags;
+ return this;
+ }
+
+ /**
+ * Sets the http/https URI for the content. See
+ * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info.
+ * @param linkUri Optional http/https URI for the content.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setLinkUri(@Nullable Uri linkUri) {
+ mLinkUri = linkUri;
+ return this;
+ }
+
+ /**
+ * Sets additional metadata.
+ * @param extras Optional bundle with additional metadata.
+ * @return this builder
+ */
+ @NonNull
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
+ * @return A new {@link Payload} instance with the data from this builder.
+ */
+ @NonNull
+ public Payload build() {
+ return new Payload(this);
+ }
+ }
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e7e28ac..c430a4d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3995,89 +3995,6 @@
/**
* @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the status bar is displayed in transient mode.
- */
- public static final int STATUS_BAR_TRANSIENT = 0x04000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the navigation bar is displayed in transient mode.
- */
- @UnsupportedAppUsage
- public static final int NAVIGATION_BAR_TRANSIENT = 0x08000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the hidden status bar would like to be shown.
- */
- public static final int STATUS_BAR_UNHIDE = 0x10000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the hidden navigation bar would like to be shown.
- */
- public static final int NAVIGATION_BAR_UNHIDE = 0x20000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the status bar is displayed in translucent mode.
- */
- public static final int STATUS_BAR_TRANSLUCENT = 0x40000000;
-
- /**
- * @hide
- *
- * NOTE: This flag may only be used in subtreeSystemUiVisibility. It is masked
- * out of the public fields to keep the undefined bits out of the developer's way.
- *
- * Flag to specify that the navigation bar is displayed in translucent mode.
- */
- public static final int NAVIGATION_BAR_TRANSLUCENT = 0x80000000;
-
- /**
- * @hide
- *
- * Makes navigation bar transparent (but not the status bar).
- */
- public static final int NAVIGATION_BAR_TRANSPARENT = 0x00008000;
-
- /**
- * @hide
- *
- * Makes status bar transparent (but not the navigation bar).
- */
- public static final int STATUS_BAR_TRANSPARENT = 0x00000008;
-
- /**
- * @hide
- *
- * Makes both status bar and navigation bar transparent.
- */
- public static final int SYSTEM_UI_TRANSPARENT = NAVIGATION_BAR_TRANSPARENT
- | STATUS_BAR_TRANSPARENT;
-
- /**
- * @hide
*/
public static final int PUBLIC_STATUS_BAR_VISIBILITY_MASK = 0x00003FF7;
@@ -4302,31 +4219,7 @@
name = "STATUS_BAR_DISABLE_RECENT"),
@ViewDebug.FlagToString(mask = STATUS_BAR_DISABLE_SEARCH,
equals = STATUS_BAR_DISABLE_SEARCH,
- name = "STATUS_BAR_DISABLE_SEARCH"),
- @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSIENT,
- equals = STATUS_BAR_TRANSIENT,
- name = "STATUS_BAR_TRANSIENT"),
- @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSIENT,
- equals = NAVIGATION_BAR_TRANSIENT,
- name = "NAVIGATION_BAR_TRANSIENT"),
- @ViewDebug.FlagToString(mask = STATUS_BAR_UNHIDE,
- equals = STATUS_BAR_UNHIDE,
- name = "STATUS_BAR_UNHIDE"),
- @ViewDebug.FlagToString(mask = NAVIGATION_BAR_UNHIDE,
- equals = NAVIGATION_BAR_UNHIDE,
- name = "NAVIGATION_BAR_UNHIDE"),
- @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSLUCENT,
- equals = STATUS_BAR_TRANSLUCENT,
- name = "STATUS_BAR_TRANSLUCENT"),
- @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSLUCENT,
- equals = NAVIGATION_BAR_TRANSLUCENT,
- name = "NAVIGATION_BAR_TRANSLUCENT"),
- @ViewDebug.FlagToString(mask = NAVIGATION_BAR_TRANSPARENT,
- equals = NAVIGATION_BAR_TRANSPARENT,
- name = "NAVIGATION_BAR_TRANSPARENT"),
- @ViewDebug.FlagToString(mask = STATUS_BAR_TRANSPARENT,
- equals = STATUS_BAR_TRANSPARENT,
- name = "STATUS_BAR_TRANSPARENT")
+ name = "STATUS_BAR_DISABLE_SEARCH")
}, formatToHexString = true)
@SystemUiVisibility
int mSystemUiVisibility;
@@ -4355,14 +4248,6 @@
STATUS_BAR_DISABLE_CLOCK,
STATUS_BAR_DISABLE_RECENT,
STATUS_BAR_DISABLE_SEARCH,
- STATUS_BAR_TRANSIENT,
- NAVIGATION_BAR_TRANSIENT,
- STATUS_BAR_UNHIDE,
- NAVIGATION_BAR_UNHIDE,
- STATUS_BAR_TRANSLUCENT,
- NAVIGATION_BAR_TRANSLUCENT,
- NAVIGATION_BAR_TRANSPARENT,
- STATUS_BAR_TRANSPARENT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SystemUiVisibility {}
@@ -5358,6 +5243,9 @@
@InputSourceClass
int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE;
+ @Nullable
+ private OnReceiveContentCallback<? extends View> mOnReceiveContentCallback;
+
/**
* Simple constructor to use when creating a view from code.
*
@@ -9113,6 +9001,36 @@
}
/**
+ * Returns the callback used for handling insertion of content into this view. See
+ * {@link #setOnReceiveContentCallback} for more info.
+ *
+ * @return The callback for handling insertion of content. Returns null if no callback has been
+ * {@link #setOnReceiveContentCallback set}.
+ */
+ @Nullable
+ public OnReceiveContentCallback<? extends View> getOnReceiveContentCallback() {
+ return mOnReceiveContentCallback;
+ }
+
+ /**
+ * Sets the callback to handle insertion of content into this view.
+ *
+ * <p>Depending on the view, this callback may be invoked for scenarios such as content
+ * insertion from the IME, Autofill, etc.
+ *
+ * <p>The callback will only be invoked if the MIME type of the content is
+ * {@link OnReceiveContentCallback#getSupportedMimeTypes declared as supported} by the callback.
+ * If the content type is not supported by the callback, the default platform handling will be
+ * executed instead.
+ *
+ * @param callback The callback to use. This can be null to reset to the default behavior.
+ */
+ public void setOnReceiveContentCallback(
+ @Nullable OnReceiveContentCallback<? extends View> callback) {
+ mOnReceiveContentCallback = callback;
+ }
+
+ /**
* Automatically fills the content of this view with the {@code value}.
*
* <p>Views support the Autofill Framework mainly by:
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4176e88..778ebb0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -33,6 +33,23 @@
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImplProto.ADDED;
+import static android.view.ViewRootImplProto.APP_VISIBLE;
+import static android.view.ViewRootImplProto.CUR_SCROLL_Y;
+import static android.view.ViewRootImplProto.DISPLAY_ID;
+import static android.view.ViewRootImplProto.HEIGHT;
+import static android.view.ViewRootImplProto.IS_ANIMATING;
+import static android.view.ViewRootImplProto.IS_DRAWING;
+import static android.view.ViewRootImplProto.LAST_WINDOW_INSETS;
+import static android.view.ViewRootImplProto.PENDING_DISPLAY_CUTOUT;
+import static android.view.ViewRootImplProto.REMOVED;
+import static android.view.ViewRootImplProto.SCROLL_Y;
+import static android.view.ViewRootImplProto.SOFT_INPUT_MODE;
+import static android.view.ViewRootImplProto.VIEW;
+import static android.view.ViewRootImplProto.VISIBLE_RECT;
+import static android.view.ViewRootImplProto.WIDTH;
+import static android.view.ViewRootImplProto.WINDOW_ATTRIBUTES;
+import static android.view.ViewRootImplProto.WIN_FRAME;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -61,6 +78,8 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.IME_FOCUS_CONTROLLER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.INSETS_CONTROLLER;
import android.Manifest;
import android.animation.LayoutTransition;
@@ -127,6 +146,8 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.InputDevice.InputSourceClass;
import android.view.InsetsState.InternalInsetsType;
import android.view.Surface.OutOfResourcesException;
@@ -164,6 +185,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.InputMethodDebug;
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.SomeArgs;
import com.android.internal.policy.DecorView;
@@ -182,6 +204,7 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
@@ -339,8 +362,6 @@
final int mTargetSdkVersion;
- int mSeq;
-
@UnsupportedAppUsage
View mView;
@@ -648,7 +669,6 @@
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
static final class SystemUiVisibilityInfo {
- int seq;
int globalVisibility;
int localValue;
int localChanges;
@@ -995,7 +1015,7 @@
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
adjustLayoutParamsForCompatibility(mWindowAttributes);
- res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
+ res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrames.frame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
@@ -1790,7 +1810,7 @@
.setParent(getRenderSurfaceControl())
.setCallsite("ViewRootImpl.getBoundsLayer")
.build();
- setBoundsLayerCrop();
+ setBoundsLayerCrop(mTransaction);
mTransaction.show(mBoundsLayer).apply();
}
return mBoundsLayer;
@@ -1818,25 +1838,41 @@
return ret;
}
- private void setBoundsLayerCrop() {
+ private void setBoundsLayerCrop(Transaction t) {
// mWinFrame is already adjusted for surface insets. So offset it and use it as
// the cropping bounds.
mTempBoundsRect.set(mWinFrame);
mTempBoundsRect.offsetTo(mWindowAttributes.surfaceInsets.left,
mWindowAttributes.surfaceInsets.top);
- mTransaction.setWindowCrop(mBoundsLayer, mTempBoundsRect);
+ t.setWindowCrop(mBoundsLayer, mTempBoundsRect);
}
/**
* Called after window layout to update the bounds surface. If the surface insets have changed
* or the surface has resized, update the bounds surface.
*/
- private void updateBoundsLayer() {
+ private boolean updateBoundsLayer(SurfaceControl.Transaction t) {
if (mBoundsLayer != null) {
- setBoundsLayerCrop();
- mTransaction.deferTransactionUntil(mBoundsLayer,
- getRenderSurfaceControl(), mSurface.getNextFrameNumber())
- .apply();
+ setBoundsLayerCrop(t);
+ t.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
+ mSurface.getNextFrameNumber());
+ return true;
+ }
+ return false;
+ }
+
+ private void prepareSurfaces(boolean sizeChanged) {
+ final SurfaceControl.Transaction t = mTransaction;
+ final SurfaceControl sc = getRenderSurfaceControl();
+ if (!sc.isValid()) return;
+
+ boolean applyTransaction = updateBoundsLayer(t);
+ if (sizeChanged) {
+ applyTransaction = true;
+ t.setBufferSize(sc, mSurfaceSize.x, mSurfaceSize.y);
+ }
+ if (applyTransaction) {
+ t.apply();
}
}
@@ -2927,7 +2963,16 @@
}
if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
- updateBoundsLayer();
+ // If the surface has been replaced, there's a chance the bounds layer is not parented
+ // to the new layer. When updating bounds layer, also reparent to the main VRI
+ // SurfaceControl to ensure it's correctly placed in the hierarchy.
+ //
+ // This needs to be done on the client side since WMS won't reparent the children to the
+ // new surface if it thinks the app is closing. WMS gets the signal that the app is
+ // stopping, but on the client side it doesn't get stopped since it's restarted quick
+ // enough. WMS doesn't want to keep around old children since they will leak when the
+ // client creates new children.
+ prepareSurfaces(surfaceSizeChanged);
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
@@ -7390,7 +7435,7 @@
frameNumber = mSurface.getNextFrameNumber();
}
- int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
+ int relayoutResult = mWindowSession.relayout(mWindow, params,
(int) (mView.getMeasuredWidth() * appScale + 0.5f),
(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
@@ -7520,6 +7565,38 @@
mView.debug();
}
+ /**
+ * Export the state of {@link ViewRootImpl} and other relevant classes into a protocol buffer
+ * output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of ViewRootImpl as defined in the parent message
+ */
+ @GuardedBy("this")
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(VIEW, Objects.toString(mView));
+ proto.write(DISPLAY_ID, mDisplay.getDisplayId());
+ proto.write(APP_VISIBLE, mAppVisible);
+ proto.write(HEIGHT, mHeight);
+ proto.write(WIDTH, mWidth);
+ proto.write(IS_ANIMATING, mIsAnimating);
+ mVisRect.dumpDebug(proto, VISIBLE_RECT);
+ proto.write(IS_DRAWING, mIsDrawing);
+ proto.write(ADDED, mAdded);
+ mWinFrame.dumpDebug(proto, WIN_FRAME);
+ mPendingDisplayCutout.get().dumpDebug(proto, PENDING_DISPLAY_CUTOUT);
+ proto.write(LAST_WINDOW_INSETS, Objects.toString(mLastWindowInsets));
+ proto.write(SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString(mSoftInputMode));
+ proto.write(SCROLL_Y, mScrollY);
+ proto.write(CUR_SCROLL_Y, mCurScrollY);
+ proto.write(REMOVED, mRemoved);
+ mWindowAttributes.dumpDebug(proto, WINDOW_ATTRIBUTES);
+ proto.end(token);
+ mInsetsController.dumpDebug(proto, INSETS_CONTROLLER);
+ mImeFocusController.dumpDebug(proto, IME_FOCUS_CONTROLLER);
+ }
+
public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
String innerPrefix = prefix + " ";
writer.println(prefix + "ViewRoot:");
@@ -8460,17 +8537,6 @@
mHandler.sendMessage(msg);
}
- // TODO(118118435): Remove this after migration
- public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
- int localValue, int localChanges) {
- SystemUiVisibilityInfo args = new SystemUiVisibilityInfo();
- args.seq = seq;
- args.globalVisibility = globalVisibility;
- args.localValue = localValue;
- args.localChanges = localChanges;
- mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
- }
-
public void dispatchCheckFocus() {
if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
// This will result in a call to checkFocus() below.
@@ -9094,6 +9160,9 @@
@Override
public void showInsets(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.showInsets(types, fromIme);
@@ -9102,6 +9171,9 @@
@Override
public void hideInsets(@InsetsType int types, boolean fromIme) {
+ if (fromIme) {
+ ImeTracing.getInstance().triggerDump();
+ }
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.hideInsets(types, fromIme);
@@ -9229,16 +9301,6 @@
}
@Override
- public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
- int localValue, int localChanges) {
- final ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.dispatchSystemUiVisibilityChanged(seq, globalVisibility,
- localValue, localChanges);
- }
- }
-
- @Override
public void dispatchWindowShown() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0d62da6..e96e98b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3994,4 +3994,15 @@
}
}
}
+
+ /**
+ * Holds the WM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.INJECT_EVENTS)
+ default void holdLock(int durationMs) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index f57ee65..59e0226 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -281,4 +281,13 @@
throw e.rethrowFromSystemServer();
}
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ try {
+ WindowManagerGlobal.getWindowManagerService().holdLock(durationMs);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index b70cb01..dbd8184 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -129,7 +129,7 @@
* IWindowSession implementation.
*/
@Override
- public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
@@ -165,18 +165,18 @@
* IWindowSession implementation. Currently this class doesn't need to support for multi-user.
*/
@Override
- public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
- return addToDisplay(window, seq, attrs, viewVisibility, displayId,
+ return addToDisplay(window, attrs, viewVisibility, displayId,
outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
outInsetsState, outActiveControls);
}
@Override
- public int addToDisplayWithoutInputChannel(android.view.IWindow window, int seq,
+ public int addToDisplayWithoutInputChannel(android.view.IWindow window,
android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId,
android.graphics.Rect outContentInsets, android.graphics.Rect outStableInsets,
android.view.InsetsState insetsState) {
@@ -223,7 +223,7 @@
}
@Override
- public int relayout(IWindow window, int seq, WindowManager.LayoutParams inAttrs,
+ public int relayout(IWindow window, WindowManager.LayoutParams inAttrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index eef2726..73636f8 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -16,7 +16,10 @@
package android.view.inputmethod;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD;
+
import android.annotation.CallSuper;
+import android.content.ClipData;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
@@ -34,6 +37,7 @@
import android.util.LogPrinter;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+import android.view.OnReceiveContentCallback;
import android.view.View;
class ComposingText implements NoCopySpan {
@@ -870,9 +874,41 @@
}
/**
- * The default implementation does nothing.
+ * Default implementation which invokes the target view's {@link OnReceiveContentCallback} if
+ * it is {@link View#setOnReceiveContentCallback set} and supports the MIME type of the given
+ * content; otherwise, simply returns false.
*/
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
- return false;
+ @SuppressWarnings("unchecked") final OnReceiveContentCallback<View> receiver =
+ (OnReceiveContentCallback<View>) mTargetView.getOnReceiveContentCallback();
+ if (receiver == null) {
+ if (DEBUG) {
+ Log.d(TAG, "Can't insert content from IME; no callback");
+ }
+ return false;
+ }
+ if (!receiver.supports(mTargetView, inputContentInfo.getDescription())) {
+ if (DEBUG) {
+ Log.d(TAG, "Can't insert content from IME; callback doesn't support MIME type: "
+ + inputContentInfo.getDescription());
+ }
+ return false;
+ }
+ if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
+ try {
+ inputContentInfo.requestPermission();
+ } catch (Exception e) {
+ Log.w(TAG, "Can't insert content from IME; requestPermission() failed", e);
+ return false;
+ }
+ }
+ final ClipData clip = new ClipData(inputContentInfo.getDescription(),
+ new ClipData.Item(inputContentInfo.getContentUri()));
+ final OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD)
+ .setLinkUri(inputContentInfo.getLinkUri())
+ .setExtras(opts)
+ .build();
+ return receiver.onReceiveContent(mTargetView, payload);
}
}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 104bc43..7dbf693 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -17,6 +17,12 @@
package android.view.inputmethod;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.inputmethod.EditorInfoProto.FIELD_ID;
+import static android.view.inputmethod.EditorInfoProto.IME_OPTIONS;
+import static android.view.inputmethod.EditorInfoProto.INPUT_TYPE;
+import static android.view.inputmethod.EditorInfoProto.PACKAGE_NAME;
+import static android.view.inputmethod.EditorInfoProto.PRIVATE_IME_OPTIONS;
+import static android.view.inputmethod.EditorInfoProto.TARGET_INPUT_METHOD_USER_ID;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -32,6 +38,7 @@
import android.text.ParcelableSpan;
import android.text.TextUtils;
import android.util.Printer;
+import android.util.proto.ProtoOutputStream;
import android.view.View;
import android.view.autofill.AutofillId;
@@ -795,6 +802,26 @@
}
/**
+ * Export the state of {@link EditorInfo} into a protocol buffer output stream.
+ *
+ * @param proto Stream to write the state to
+ * @param fieldId FieldId of ViewRootImpl as defined in the parent message
+ * @hide
+ */
+ public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ proto.write(INPUT_TYPE, inputType);
+ proto.write(IME_OPTIONS, imeOptions);
+ proto.write(PRIVATE_IME_OPTIONS, privateImeOptions);
+ proto.write(PACKAGE_NAME, packageName);
+ proto.write(FIELD_ID, this.fieldId);
+ if (targetInputMethodUser != null) {
+ proto.write(TARGET_INPUT_METHOD_USER_ID, targetInputMethodUser.getIdentifier());
+ }
+ proto.end(token);
+ }
+
+ /**
* Write debug output of this object.
*/
public void dump(Printer pw, String prefix) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 8adb7e5..b8f04159 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,6 +18,17 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.util.imetracing.ImeTracing.PROTO_ARG;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.DISPLAY_ID;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.EDITOR_INFO;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.INPUT_METHOD_MANAGER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientSideProto.VIEW_ROOT_IMPL;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ClientsProto.CLIENT;
+import static android.view.inputmethod.InputMethodManagerProto.ACTIVE;
+import static android.view.inputmethod.InputMethodManagerProto.CUR_ID;
+import static android.view.inputmethod.InputMethodManagerProto.FULLSCREEN_MODE;
+import static android.view.inputmethod.InputMethodManagerProto.SERVED_CONNECTING;
import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION;
@@ -62,6 +73,8 @@
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.ImeFocusController;
import android.view.ImeInsetsSourceConsumer;
@@ -564,6 +577,7 @@
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags) {
final View servedView;
+ ImeTracing.getInstance().triggerDump();
synchronized (mH) {
mCurrentTextBoxAttribute = null;
mCompletions = null;
@@ -1084,6 +1098,11 @@
mH.obtainMessage(MSG_UPDATE_ACTIVITY_VIEW_TO_SCREEN_MATRIX, bindSequence, 0,
matrixValues).sendToTarget();
}
+
+ @Override
+ public void setImeTraceEnabled(boolean enabled) {
+ ImeTracing.getInstance().setEnabled(enabled);
+ }
};
final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
@@ -1652,6 +1671,7 @@
* {@link #RESULT_HIDDEN}.
*/
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
+ ImeTracing.getInstance().triggerDump();
// Re-dispatch if there is a context mismatch.
final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
if (fallbackImm != null) {
@@ -1757,6 +1777,7 @@
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
ResultReceiver resultReceiver) {
+ ImeTracing.getInstance().triggerDump();
checkFocus();
synchronized (mH) {
final View servedView = getServedViewLocked();
@@ -3108,6 +3129,10 @@
}
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ if (processDump(fd, args)) {
+ return;
+ }
+
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method client state for " + this + ":");
@@ -3202,4 +3227,74 @@
return sb.toString();
}
+
+ /**
+ * Checks the args to see if a proto-based ime dump was requested and writes the client side
+ * ime dump to the given {@link FileDescriptor}.
+ *
+ * @return {@code true} if a proto-based ime dump was requested.
+ */
+ private boolean processDump(final FileDescriptor fd, final String[] args) {
+ if (args == null) {
+ return false;
+ }
+
+ for (String arg : args) {
+ if (arg.equals(PROTO_ARG)) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ dumpProto(proto);
+ proto.flush();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Write the proto dump for all displays associated with this client.
+ *
+ * @param proto The proto stream to which the dumps are written.
+ * @hide
+ */
+ public static void dumpProto(ProtoOutputStream proto) {
+ for (int i = sInstanceMap.size() - 1; i >= 0; i--) {
+ InputMethodManager imm = sInstanceMap.valueAt(i);
+ imm.dumpDebug(proto);
+ }
+ }
+
+ /**
+ * Write the proto dump of various client side components to the provided
+ * {@link ProtoOutputStream}.
+ *
+ * @param proto The proto stream to which the dumps are written.
+ * @hide
+ */
+ @GuardedBy("mH")
+ public void dumpDebug(ProtoOutputStream proto) {
+ if (mCurMethod == null) {
+ return;
+ }
+
+ final long clientDumpToken = proto.start(CLIENT);
+ proto.write(DISPLAY_ID, mDisplayId);
+ final long token = proto.start(INPUT_METHOD_MANAGER);
+ synchronized (mH) {
+ proto.write(CUR_ID, mCurId);
+ proto.write(FULLSCREEN_MODE, mFullscreenMode);
+ proto.write(ACTIVE, mActive);
+ proto.write(SERVED_CONNECTING, mServedConnecting);
+ proto.end(token);
+ if (mCurRootView != null) {
+ mCurRootView.dumpDebug(proto, VIEW_ROOT_IMPL);
+ }
+ if (mCurrentTextBoxAttribute != null) {
+ mCurrentTextBoxAttribute.dumpDebug(proto, EDITOR_INFO);
+ }
+ if (mImeInsetsConsumer != null) {
+ mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER);
+ }
+ }
+ proto.end(clientDumpToken);
+ }
}
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index 8224e0e..12fe626 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Parcel;
@@ -53,6 +54,7 @@
/**
* @hide
*/
+ @TestApi
public static final int SUBTYPE_ID_NONE = 0;
private static final String SUBTYPE_LANGUAGE_TAG_NONE = "";
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index acb35d6..cd70a31 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -259,6 +260,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @TestApi
public boolean isSpellCheckerEnabled() {
try {
return mService.isSpellCheckerEnabled(mUserId);
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 60f8bb7..fa19521 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,6 +16,8 @@
package android.widget;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
+
import android.R;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
@@ -96,6 +98,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnReceiveContentCallback;
import android.view.SubMenu;
import android.view.View;
import android.view.View.DragShadowBuilder;
@@ -2851,9 +2854,11 @@
try {
final int originalLength = mTextView.getText().length();
Selection.setSelection((Spannable) mTextView.getText(), offset);
- ClipData clip = event.getClipData();
- mTextView.getRichContentReceiver().onReceive(mTextView, clip,
- RichContentReceiver.SOURCE_DRAG_AND_DROP, 0);
+ final ClipData clip = event.getClipData();
+ final OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_DRAG_AND_DROP)
+ .build();
+ mTextView.onReceiveContent(payload);
if (dragDropIntoItself) {
deleteSourceAfterLocalDrop(dragLocalState, offset, originalLength);
}
diff --git a/core/java/android/widget/RichContentReceiver.java b/core/java/android/widget/RichContentReceiver.java
deleted file mode 100644
index 80a0562..0000000
--- a/core/java/android/widget/RichContentReceiver.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.widget;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.view.View;
-import android.view.inputmethod.InputConnection;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Set;
-
-/**
- * Callback for apps to implement handling for insertion of rich content. "Rich content" here refers
- * to both text and non-text content: plain text, styled text, HTML, images, videos, audio files,
- * etc.
- *
- * <p>This callback can be attached to different types of UI components. For editable
- * {@link TextView} components, implementations should typically wrap
- * {@link TextView#DEFAULT_RICH_CONTENT_RECEIVER}.
- *
- * <p>Example implementation:<br>
- * <pre class="prettyprint">
- * public class MyRichContentReceiver implements RichContentReceiver<TextView> {
- *
- * private static final Set<String> SUPPORTED_MIME_TYPES = Collections.unmodifiableSet(
- * Set.of("text/*", "image/gif", "image/png", "image/jpg"));
- *
- * @NonNull
- * @Override
- * public Set<String> getSupportedMimeTypes() {
- * return SUPPORTED_MIME_TYPES;
- * }
- *
- * @Override
- * public boolean onReceive(@NonNull TextView textView, @NonNull ClipData clip,
- * int source, int flags) {
- * if (clip.getDescription().hasMimeType("image/*")) {
- * return receiveImage(textView, clip);
- * }
- * return TextView.DEFAULT_RICH_CONTENT_RECEIVER.onReceive(textView, clip, source);
- * }
- *
- * private boolean receiveImage(@NonNull TextView textView, @NonNull ClipData clip) {
- * // ... app-specific logic to handle the content URI in the clip ...
- * }
- * }
- * </pre>
- *
- * @param <T> The type of {@link View} with which this receiver can be associated.
- */
-@SuppressLint("CallbackMethodName")
-public interface RichContentReceiver<T extends View> {
- /**
- * Specifies the UI through which content is being inserted.
- *
- * @hide
- */
- @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
- SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
- @Retention(RetentionPolicy.SOURCE)
- @interface Source {}
-
- /**
- * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
- * "Paste as plain text" action in the insertion/selection menu).
- */
- int SOURCE_CLIPBOARD = 0;
-
- /**
- * Specifies that the operation was triggered from the soft keyboard (also known as input method
- * editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard for more
- * info.
- */
- int SOURCE_INPUT_METHOD = 1;
-
- /**
- * Specifies that the operation was triggered by the drag/drop framework. See
- * https://developer.android.com/guide/topics/ui/drag-drop for more info.
- */
- int SOURCE_DRAG_AND_DROP = 2;
-
- /**
- * Specifies that the operation was triggered by the autofill framework. See
- * https://developer.android.com/guide/topics/text/autofill for more info.
- */
- int SOURCE_AUTOFILL = 3;
-
- /**
- * Specifies that the operation was triggered by a result from a
- * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection menu.
- */
- int SOURCE_PROCESS_TEXT = 4;
-
- /**
- * Flags to configure the insertion behavior.
- *
- * @hide
- */
- @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
- @Retention(RetentionPolicy.SOURCE)
- @interface Flags {}
-
- /**
- * Flag for {@link #onReceive} requesting that the content should be converted to plain text
- * prior to inserting.
- */
- int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
-
- /**
- * Insert the given clip.
- *
- * <p>For editable {@link TextView} components, this function will be invoked for the
- * following scenarios:
- * <ol>
- * <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
- * insertion/selection menu)
- * <li>Content insertion from the keyboard ({@link InputConnection#commitContent})
- * <li>Drag and drop ({@link View#onDragEvent})
- * <li>Autofill, when the type for the field is
- * {@link android.view.View.AutofillType#AUTOFILL_TYPE_RICH_CONTENT}
- * </ol>
- *
- * <p>For text, if the view has a selection, the selection should be overwritten by the
- * clip; if there's no selection, this method should insert the content at the current
- * cursor position.
- *
- * <p>For rich content (e.g. an image), this function may insert the content inline, or it may
- * add the content as an attachment (could potentially go into a completely separate view).
- *
- * <p>This function may be invoked with a clip whose MIME type is not in the list of supported
- * types returned by {@link #getSupportedMimeTypes()}. This provides the opportunity to
- * implement custom fallback logic if desired.
- *
- * @param view The view where the content insertion was requested.
- * @param clip The clip to insert.
- * @param source The trigger of the operation.
- * @param flags Optional flags to configure the insertion behavior. Use 0 for default
- * behavior. See {@code FLAG_} constants on this interface for other options.
- * @return Returns true if the clip was inserted.
- */
- boolean onReceive(@NonNull T view, @NonNull ClipData clip, @Source int source, int flags);
-
- /**
- * Returns the MIME types that can be handled by this callback.
- *
- * <p>Different platform features (e.g. pasting from the clipboard, inserting stickers from the
- * keyboard, etc) may use this function to conditionally alter their behavior. For example, the
- * keyboard may choose to hide its UI for inserting GIFs if the input field that has focus has
- * a {@link RichContentReceiver} set and the MIME types returned from this function don't
- * include "image/gif".
- *
- * @return An immutable set with the MIME types supported by this callback. The returned
- * MIME types may contain wildcards such as "text/*", "image/*", etc.
- */
- @NonNull
- Set<String> getSupportedMimeTypes();
-
- /**
- * Returns true if the MIME type of the given clip is {@link #getSupportedMimeTypes supported}
- * by this receiver.
- *
- * @hide
- */
- default boolean supports(@NonNull ClipDescription description) {
- for (String supportedMimeType : getSupportedMimeTypes()) {
- if (description.hasMimeType(supportedMimeType)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns true if this receiver {@link #getSupportedMimeTypes supports} non-text content, such
- * as images.
- *
- * @hide
- */
- default boolean supportsNonTextContent() {
- for (String supportedMimeType : getSupportedMimeTypes()) {
- if (!supportedMimeType.startsWith("text/")) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns the symbolic name of the given source.
- *
- * @hide
- */
- static String sourceToString(@Source int source) {
- switch (source) {
- case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
- case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
- case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
- case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL";
- case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT";
- }
- return String.valueOf(source);
- }
-
- /**
- * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
- *
- * @hide
- */
- static String flagsToString(@Flags int flags) {
- if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
- return "FLAG_CONVERT_TO_PLAIN_TEXT";
- }
- return String.valueOf(flags);
- }
-}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6f14dfb..52a3f41 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,12 +17,15 @@
package android.widget;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_CLIPBOARD;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_PROCESS_TEXT;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
-import static android.widget.RichContentReceiver.SOURCE_PROCESS_TEXT;
import android.R;
import android.annotation.CallSuper;
@@ -39,6 +42,7 @@
import android.annotation.Size;
import android.annotation.StringRes;
import android.annotation.StyleRes;
+import android.annotation.TestApi;
import android.annotation.XmlRes;
import android.app.Activity;
import android.app.PendingIntent;
@@ -149,6 +153,7 @@
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.OnReceiveContentCallback;
import android.view.PointerIcon;
import android.view.View;
import android.view.ViewConfiguration;
@@ -426,7 +431,8 @@
/**
* @hide
*/
- static final int PROCESS_TEXT_REQUEST_CODE = 100;
+ @TestApi
+ public static final int PROCESS_TEXT_REQUEST_CODE = 100;
/**
* Return code of {@link #doKeyDown}.
@@ -735,6 +741,10 @@
private boolean mLocalesChanged = false;
private int mTextSizeUnit = -1;
+ // True if force bold text feature is enabled. This feature makes all text bolder.
+ private boolean mForceBoldTextEnabled;
+ private Typeface mOriginalTypeface;
+
// True if setKeyListener() has been explicitly called
private boolean mListenerChanged = false;
// True if internationalized input should be used for numbers and date and time.
@@ -882,12 +892,10 @@
/**
* The default content insertion callback used by {@link TextView}. See
- * {@link #setRichContentReceiver} for more info.
+ * {@link #setOnReceiveContentCallback} for more info.
*/
- public static final @NonNull RichContentReceiver<TextView> DEFAULT_RICH_CONTENT_RECEIVER =
- TextViewRichContentReceiver.INSTANCE;
-
- private RichContentReceiver<TextView> mRichContentReceiver = DEFAULT_RICH_CONTENT_RECEIVER;
+ private static final TextViewOnReceiveContentCallback DEFAULT_ON_RECEIVE_CONTENT_CALLBACK =
+ new TextViewOnReceiveContentCallback();
private static final int DEVICE_PROVISIONED_UNKNOWN = 0;
private static final int DEVICE_PROVISIONED_NO = 1;
@@ -1645,6 +1653,8 @@
attributes.mTypefaceIndex = MONOSPACE;
}
+ mForceBoldTextEnabled = getContext().getResources().getConfiguration().forceBoldText
+ == Configuration.FORCE_BOLD_TEXT_YES;
applyTextAppearance(attributes);
if (isPassword) {
@@ -2138,15 +2148,20 @@
/**
* @hide
*/
+ @TestApi
@Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (requestCode == PROCESS_TEXT_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT);
if (result != null) {
if (isTextEditable()) {
ClipData clip = ClipData.newPlainText("", result);
- mRichContentReceiver.onReceive(this, clip, SOURCE_PROCESS_TEXT, 0);
+ OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(
+ clip, SOURCE_PROCESS_TEXT)
+ .build();
+ onReceiveContent(payload);
if (mEditor != null) {
mEditor.refreshTextActionMode();
}
@@ -4267,6 +4282,14 @@
invalidate();
}
}
+ if (newConfig.forceBoldText == Configuration.FORCE_BOLD_TEXT_YES) {
+ mForceBoldTextEnabled = true;
+ setTypeface(getTypeface());
+ } else if (newConfig.forceBoldText == Configuration.FORCE_BOLD_TEXT_NO
+ || newConfig.forceBoldText == Configuration.FORCE_BOLD_TEXT_UNDEFINED) {
+ mForceBoldTextEnabled = false;
+ setTypeface(getTypeface());
+ }
}
/**
@@ -4418,6 +4441,14 @@
* @attr ref android.R.styleable#TextView_textStyle
*/
public void setTypeface(@Nullable Typeface tf) {
+ mOriginalTypeface = tf;
+ if (mForceBoldTextEnabled) {
+ int newWeight = tf != null ? tf.getWeight() + 300 : 400;
+ newWeight = Math.min(newWeight, 1000);
+ int typefaceStyle = tf != null ? tf.getStyle() : 0;
+ boolean italic = (typefaceStyle & Typeface.ITALIC) != 0;
+ tf = Typeface.create(tf, newWeight, italic);
+ }
if (mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf);
@@ -4441,7 +4472,7 @@
*/
@InspectableProperty
public Typeface getTypeface() {
- return mTextPaint.getTypeface();
+ return mOriginalTypeface;
}
/**
@@ -8722,9 +8753,10 @@
outAttrs.initialSelEnd = getSelectionEnd();
outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType());
outAttrs.setInitialSurroundingText(mText);
- int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion > Build.VERSION_CODES.R) {
- outAttrs.contentMimeTypes = mRichContentReceiver.getSupportedMimeTypes()
+ // If a custom `OnReceiveContentCallback` is set, pass its supported MIME types.
+ OnReceiveContentCallback<TextView> receiver = getOnReceiveContentCallback();
+ if (receiver != null) {
+ outAttrs.contentMimeTypes = receiver.getSupportedMimeTypes(this)
.toArray(new String[0]);
}
return ic;
@@ -11827,7 +11859,7 @@
Log.w(LOG_TAG, "cannot autofill non-editable TextView: " + this);
return;
}
- ClipData clip;
+ final ClipData clip;
if (value.isRichContent()) {
clip = value.getRichContentValue();
} else if (value.isText()) {
@@ -11837,22 +11869,14 @@
+ " cannot be autofilled into " + this);
return;
}
- mRichContentReceiver.onReceive(this, clip, RichContentReceiver.SOURCE_AUTOFILL, 0);
+ final OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+ onReceiveContent(payload);
}
@Override
public @AutofillType int getAutofillType() {
- if (!isTextEditable()) {
- return AUTOFILL_TYPE_NONE;
- }
- final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion <= Build.VERSION_CODES.R) {
- return AUTOFILL_TYPE_TEXT;
- }
- // TODO(b/147301047): Update autofill framework code to check the target SDK of the autofill
- // provider and force the type AUTOFILL_TYPE_TEXT for providers that target older SDKs.
- return mRichContentReceiver.supportsNonTextContent() ? AUTOFILL_TYPE_RICH_CONTENT
- : AUTOFILL_TYPE_TEXT;
+ return isTextEditable() ? AUTOFILL_TYPE_TEXT : AUTOFILL_TYPE_NONE;
}
/**
@@ -12913,8 +12937,11 @@
if (clip == null) {
return;
}
- int flags = withFormatting ? 0 : RichContentReceiver.FLAG_CONVERT_TO_PLAIN_TEXT;
- mRichContentReceiver.onReceive(this, clip, RichContentReceiver.SOURCE_CLIPBOARD, flags);
+ final OnReceiveContentCallback.Payload payload =
+ new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_CLIPBOARD)
+ .setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT)
+ .build();
+ onReceiveContent(payload);
sLastCutCopyOrTextChangedTime = 0;
}
@@ -13697,43 +13724,58 @@
}
/**
- * Returns the callback that handles insertion of content into this view (e.g. pasting from
- * the clipboard). See {@link #setRichContentReceiver} for more info.
+ * Returns the callback used for handling insertion of content into this view. See
+ * {@link #setOnReceiveContentCallback} for more info.
*
- * @return The callback that this view is using to handle insertion of content. Returns
- * {@link #DEFAULT_RICH_CONTENT_RECEIVER} if no custom callback has been
- * {@link #setRichContentReceiver set}.
+ * @return The callback for handling insertion of content. Returns null if no callback has been
+ * {@link #setOnReceiveContentCallback set}.
*/
- @NonNull
- public RichContentReceiver<TextView> getRichContentReceiver() {
- return mRichContentReceiver;
+ @SuppressWarnings("unchecked")
+ @Nullable
+ @Override
+ public OnReceiveContentCallback<TextView> getOnReceiveContentCallback() {
+ return (OnReceiveContentCallback<TextView>) super.getOnReceiveContentCallback();
}
/**
* Sets the callback to handle insertion of content into this view.
*
- * <p>"Content" and "rich content" here refers to both text and non-text: plain text, styled
- * text, HTML, images, videos, audio files, etc.
- *
- * <p>The callback configured here should typically wrap {@link #DEFAULT_RICH_CONTENT_RECEIVER}
- * to provide consistent behavior for text content.
- *
* <p>This callback will be invoked for the following scenarios:
* <ol>
* <li>Paste from the clipboard (e.g. "Paste" or "Paste as plain text" action in the
* insertion/selection menu)
- * <li>Content insertion from the keyboard ({@link InputConnection#commitContent})
- * <li>Drag and drop ({@link View#onDragEvent})
- * <li>Autofill, when the type for the field is
- * {@link android.view.View.AutofillType#AUTOFILL_TYPE_RICH_CONTENT}
+ * <li>Content insertion from the keyboard (from {@link InputConnection#commitContent})
+ * <li>Drag and drop (drop events from {@link #onDragEvent(DragEvent)})
+ * <li>Autofill (from {@link #autofill(AutofillValue)})
+ * <li>{@link Intent#ACTION_PROCESS_TEXT} replacement
* </ol>
*
- * @param receiver The callback to use. This can be {@link #DEFAULT_RICH_CONTENT_RECEIVER} to
- * reset to the default behavior.
+ * <p>The callback will only be invoked if the MIME type of the content is
+ * {@link OnReceiveContentCallback#getSupportedMimeTypes declared as supported} by the callback.
+ * If the content type is not supported by the callback, the default platform handling will be
+ * executed instead.
+ *
+ * @param callback The callback to use. This can be null to reset to the default behavior.
*/
- public void setRichContentReceiver(@NonNull RichContentReceiver<TextView> receiver) {
- mRichContentReceiver = Objects.requireNonNull(receiver,
- "RichContentReceiver should not be null.");
+ @Override
+ public void setOnReceiveContentCallback(
+ @Nullable OnReceiveContentCallback<? extends View> callback) {
+ super.setOnReceiveContentCallback(callback);
+ }
+
+ /**
+ * Handles the request to insert content using the configured callback or the default callback.
+ *
+ * @hide
+ */
+ void onReceiveContent(@NonNull OnReceiveContentCallback.Payload payload) {
+ OnReceiveContentCallback<TextView> receiver = getOnReceiveContentCallback();
+ ClipDescription description = payload.getClip().getDescription();
+ if (receiver != null && receiver.supports(this, description)) {
+ receiver.onReceiveContent(this, payload);
+ } else {
+ DEFAULT_ON_RECEIVE_CONTENT_CALLBACK.onReceiveContent(this, payload);
+ }
}
private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
diff --git a/core/java/android/widget/TextViewRichContentReceiver.java b/core/java/android/widget/TextViewOnReceiveContentCallback.java
similarity index 71%
rename from core/java/android/widget/TextViewRichContentReceiver.java
rename to core/java/android/widget/TextViewOnReceiveContentCallback.java
index 4f2d954..35618cb 100644
--- a/core/java/android/widget/TextViewRichContentReceiver.java
+++ b/core/java/android/widget/TextViewOnReceiveContentCallback.java
@@ -16,7 +16,12 @@
package android.widget;
+import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL;
+import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP;
+
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.Context;
import android.text.Editable;
@@ -24,54 +29,45 @@
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.util.Log;
+import android.view.OnReceiveContentCallback;
+import android.view.OnReceiveContentCallback.Payload.Flags;
+import android.view.OnReceiveContentCallback.Payload.Source;
import java.util.Collections;
import java.util.Set;
/**
- * Default implementation of {@link RichContentReceiver} for editable {@link TextView} components.
- * This class handles insertion of text (plain text, styled text, HTML, etc) but not images or other
- * rich content. Typically this class will be used as a delegate by custom implementations of
- * {@link RichContentReceiver}, to provide consistent behavior for insertion of text while
- * implementing custom behavior for insertion of other content (images, etc). See
- * {@link TextView#DEFAULT_RICH_CONTENT_RECEIVER}.
- *
- * @hide
+ * Default implementation of {@link android.view.OnReceiveContentCallback} for editable
+ * {@link TextView} components. This class handles insertion of text (plain text, styled text, HTML,
+ * etc) but not images or other content. This class can be used as a base class for an
+ * implementation of {@link android.view.OnReceiveContentCallback} for a {@link TextView}, to
+ * provide consistent behavior for insertion of text.
*/
-final class TextViewRichContentReceiver implements RichContentReceiver<TextView> {
- static final TextViewRichContentReceiver INSTANCE = new TextViewRichContentReceiver();
-
- private static final String LOG_TAG = "RichContentReceiver";
+public class TextViewOnReceiveContentCallback implements OnReceiveContentCallback<TextView> {
+ private static final String LOG_TAG = "OnReceiveContent";
private static final Set<String> MIME_TYPES_ALL_TEXT = Collections.singleton("text/*");
+ @SuppressLint("CallbackMethodName")
+ @NonNull
@Override
- public Set<String> getSupportedMimeTypes() {
+ public Set<String> getSupportedMimeTypes(@NonNull TextView view) {
return MIME_TYPES_ALL_TEXT;
}
@Override
- public boolean onReceive(@NonNull TextView textView, @NonNull ClipData clip,
- @Source int source, @Flags int flags) {
+ public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) {
if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
- StringBuilder sb = new StringBuilder("onReceive: clip=");
- if (clip.getDescription() == null) {
- sb.append("null");
- } else {
- clip.getDescription().toShortStringTypesOnly(sb);
- }
- sb.append(", source=").append(RichContentReceiver.sourceToString(source));
- sb.append(", flags=").append(RichContentReceiver.flagsToString(flags));
- Log.d(LOG_TAG, sb.toString());
+ Log.d(LOG_TAG, "onReceive:" + payload);
}
+ ClipData clip = payload.getClip();
+ @Source int source = payload.getSource();
+ @Flags int flags = payload.getFlags();
if (source == SOURCE_AUTOFILL) {
- return onReceiveForAutofill(textView, clip, flags);
+ return onReceiveForAutofill(view, clip, flags);
}
if (source == SOURCE_DRAG_AND_DROP) {
- return onReceiveForDragAndDrop(textView, clip, flags);
- }
- if (source == SOURCE_INPUT_METHOD && !supports(clip.getDescription())) {
- return false;
+ return onReceiveForDragAndDrop(view, clip, flags);
}
// The code here follows the original paste logic from TextView:
@@ -79,8 +75,8 @@
// In particular, multiple items within the given ClipData will trigger separate calls to
// replace/insert. This is to preserve the original behavior with respect to TextWatcher
// notifications fired from SpannableStringBuilder when replace/insert is called.
- final Editable editable = (Editable) textView.getText();
- final Context context = textView.getContext();
+ final Editable editable = (Editable) view.getText();
+ final Context context = view.getContext();
boolean didFirst = false;
for (int i = 0; i < clip.getItemCount(); i++) {
CharSequence itemText;
@@ -100,7 +96,7 @@
}
}
}
- return didFirst;
+ return true;
}
private static void replaceSelection(@NonNull Editable editable,
@@ -128,7 +124,7 @@
@NonNull ClipData clip, @Flags int flags) {
final CharSequence text = coerceToText(clip, textView.getContext(), flags);
if (text.length() == 0) {
- return false;
+ return true;
}
replaceSelection((Editable) textView.getText(), text);
return true;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 944f2ec..61a625e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1771,7 +1771,7 @@
case ChooserListAdapter.TARGET_CALLER:
case ChooserListAdapter.TARGET_STANDARD:
cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
- value -= currentListAdapter.getSelectableServiceTargetCount();
+ value -= currentListAdapter.getSurfacedTargetInfo().size();
numCallerProvided = currentListAdapter.getCallerTargetCount();
getChooserActivityLogger().logShareTargetSelected(
SELECTION_TYPE_APP,
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4a0e26a..308af99 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -145,7 +145,7 @@
private static final boolean DEBUG = false;
public static final boolean DEBUG_ENERGY = false;
private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
- private static final boolean DEBUG_BINDER_STATS = true;
+ private static final boolean DEBUG_BINDER_STATS = false;
private static final boolean DEBUG_MEMORY = false;
private static final boolean DEBUG_HISTORY = false;
private static final boolean USE_OLD_HISTORY = false; // for debugging.
@@ -156,7 +156,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- static final int VERSION = 188 + (USE_OLD_HISTORY ? 1000 : 0);
+ static final int VERSION = 189 + (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
@@ -221,7 +221,8 @@
@VisibleForTesting
protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
@VisibleForTesting
- protected SystemServerCpuThreadReader mSystemServerCpuThreadReader;
+ protected SystemServerCpuThreadReader mSystemServerCpuThreadReader =
+ SystemServerCpuThreadReader.create();
private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
= new KernelMemoryBandwidthStats();
@@ -1014,14 +1015,21 @@
private long[] mCpuFreqs;
/**
+ * Times spent by the system server process grouped by cluster and CPU speed.
+ */
+ private LongSamplingCounterArray mSystemServerCpuTimesUs;
+
+ private long[] mTmpSystemServerCpuTimesUs;
+
+ /**
* Times spent by the system server threads grouped by cluster and CPU speed.
*/
- private LongSamplingCounter[][] mSystemServerThreadCpuTimesUs;
+ private LongSamplingCounterArray mSystemServerThreadCpuTimesUs;
/**
* Times spent by the system server threads handling incoming binder requests.
*/
- private LongSamplingCounter[][] mBinderThreadCpuTimesUs;
+ private LongSamplingCounterArray mBinderThreadCpuTimesUs;
@VisibleForTesting
protected PowerProfile mPowerProfile;
@@ -10610,8 +10618,6 @@
firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
}
- mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
-
if (mEstimatedBatteryCapacity == -1) {
// Initialize the estimated battery capacity to a known preset one.
mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
@@ -11291,6 +11297,7 @@
mTmpRailStats.reset();
+ resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs);
resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs);
resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
@@ -12421,38 +12428,58 @@
SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
mSystemServerCpuThreadReader.readDelta();
+ if (systemServiceCpuThreadTimes == null) {
+ return;
+ }
- int index = 0;
int numCpuClusters = mPowerProfile.getNumCpuClusters();
- if (mSystemServerThreadCpuTimesUs == null) {
- mSystemServerThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
- mBinderThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
+ if (mSystemServerCpuTimesUs == null) {
+ mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+ mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+ mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
}
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
- if (mSystemServerThreadCpuTimesUs[cluster] == null) {
- mSystemServerThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
- mBinderThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
- for (int speed = 0; speed < numSpeeds; speed++) {
- mSystemServerThreadCpuTimesUs[cluster][speed] =
- new LongSamplingCounter(mOnBatteryTimeBase);
- mBinderThreadCpuTimesUs[cluster][speed] =
- new LongSamplingCounter(mOnBatteryTimeBase);
- }
- }
- for (int speed = 0; speed < numSpeeds; speed++) {
- mSystemServerThreadCpuTimesUs[cluster][speed].addCountLocked(
- systemServiceCpuThreadTimes.threadCpuTimesUs[index]);
- mBinderThreadCpuTimesUs[cluster][speed].addCountLocked(
- systemServiceCpuThreadTimes.binderThreadCpuTimesUs[index]);
- index++;
- }
+ mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs);
+ mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+
+ long totalCpuTimeAllThreads = 0;
+ for (int index = systemServiceCpuThreadTimes.threadCpuTimesUs.length - 1; index >= 0;
+ index--) {
+ totalCpuTimeAllThreads += systemServiceCpuThreadTimes.threadCpuTimesUs[index];
}
+
+ // Estimate per cluster per frequency CPU time for the entire process
+ // by distributing the total process CPU time proportionately to how much
+ // CPU time its threads took on those clusters/frequencies. This algorithm
+ // works more accurately when when we have equally distributed concurrency.
+ // TODO(b/169279846): obtain actual process CPU times from the kernel
+ long processCpuTime = systemServiceCpuThreadTimes.processCpuTimeUs;
+ if (mTmpSystemServerCpuTimesUs == null) {
+ mTmpSystemServerCpuTimesUs =
+ new long[systemServiceCpuThreadTimes.threadCpuTimesUs.length];
+ }
+ for (int index = systemServiceCpuThreadTimes.threadCpuTimesUs.length - 1; index >= 0;
+ index--) {
+ mTmpSystemServerCpuTimesUs[index] =
+ processCpuTime * systemServiceCpuThreadTimes.threadCpuTimesUs[index]
+ / totalCpuTimeAllThreads;
+
+ }
+
+ mSystemServerCpuTimesUs.addCountLocked(mTmpSystemServerCpuTimesUs);
+
if (DEBUG_BINDER_STATS) {
Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
- long binderThreadTimeMs = 0;
+ long totalCpuTimeMs = 0;
long totalThreadTimeMs = 0;
+ long binderThreadTimeMs = 0;
int cpuIndex = 0;
+ final long[] systemServerCpuTimesUs =
+ mSystemServerCpuTimesUs.getCountsLocked(0);
+ final long[] systemServerThreadCpuTimesUs =
+ mSystemServerThreadCpuTimesUs.getCountsLocked(0);
+ final long[] binderThreadCpuTimesUs =
+ mBinderThreadCpuTimesUs.getCountsLocked(0);
+ int index = 0;
for (int cluster = 0; cluster < numCpuClusters; cluster++) {
StringBuilder sb = new StringBuilder();
sb.append("cpu").append(cpuIndex).append(": [");
@@ -12461,15 +12488,14 @@
if (speed != 0) {
sb.append(", ");
}
- long totalCountMs =
- mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked(0) / 1000;
- long binderCountMs = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0)
- / 1000;
+ long totalCountMs = systemServerThreadCpuTimesUs[index] / 1000;
+ long binderCountMs = binderThreadCpuTimesUs[index] / 1000;
sb.append(String.format("%d/%d(%.1f%%)",
binderCountMs,
totalCountMs,
totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0));
+ totalCpuTimeMs += systemServerCpuTimesUs[index] / 1000;
totalThreadTimeMs += totalCountMs;
binderThreadTimeMs += binderCountMs;
index++;
@@ -12477,6 +12503,8 @@
cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
Slog.d(TAG, sb.toString());
}
+
+ Slog.d(TAG, "Total system server CPU time (ms): " + totalCpuTimeMs);
Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs);
Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
binderThreadTimeMs,
@@ -13715,7 +13743,7 @@
@Override
- public long getSystemServiceTimeAtCpuSpeed(int cluster, int step) {
+ public long[] getSystemServiceTimeAtCpuSpeeds() {
// Estimates the time spent by the system server handling incoming binder requests.
//
// The data that we can get from the kernel is this:
@@ -13731,7 +13759,7 @@
// - These 10 threads spent 1000 ms of CPU time in aggregate
// - Of the 10 threads 4 were execute exclusively incoming binder calls.
// - These 4 "binder" threads consumed 600 ms of CPU time in aggregate
- // - The real time spent by the system server UID doing all of this is, say, 200 ms.
+ // - The real time spent by the system server process doing all of this is, say, 200 ms.
//
// We will assume that power consumption is proportional to the time spent by the CPU
// across all threads. This is a crude assumption, but we don't have more detailed data.
@@ -13745,41 +13773,29 @@
// of the total power consumed by incoming binder calls for the given cluster/speed
// combination.
- if (mSystemServerThreadCpuTimesUs == null) {
- return 0;
+ if (mSystemServerCpuTimesUs == null) {
+ return null;
}
- if (cluster < 0 || cluster >= mSystemServerThreadCpuTimesUs.length) {
- return 0;
- }
-
- final LongSamplingCounter[] threadTimesForCluster = mSystemServerThreadCpuTimesUs[cluster];
-
- if (step < 0 || step >= threadTimesForCluster.length) {
- return 0;
- }
-
- Uid systemUid = mUidStats.get(Process.SYSTEM_UID);
- if (systemUid == null) {
- return 0;
- }
-
- final long uidTimeAtCpuSpeedUs = systemUid.getTimeAtCpuSpeed(cluster, step,
+ final long[] systemServerCpuTimesUs = mSystemServerCpuTimesUs.getCountsLocked(
BatteryStats.STATS_SINCE_CHARGED);
- if (uidTimeAtCpuSpeedUs == 0) {
- return 0;
- }
-
- final long uidThreadTimeUs =
- threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
-
- if (uidThreadTimeUs == 0) {
- return 0;
- }
-
- final long binderThreadTimeUs = mBinderThreadCpuTimesUs[cluster][step].getCountLocked(
+ final long [] systemServerThreadCpuTimesUs = mSystemServerThreadCpuTimesUs.getCountsLocked(
BatteryStats.STATS_SINCE_CHARGED);
- return uidTimeAtCpuSpeedUs * binderThreadTimeUs / uidThreadTimeUs;
+ final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
+ BatteryStats.STATS_SINCE_CHARGED);
+
+ final int size = systemServerCpuTimesUs.length;
+ final long[] results = new long[size];
+
+ for (int i = 0; i < size; i++) {
+ if (systemServerThreadCpuTimesUs[i] == 0) {
+ continue;
+ }
+
+ results[i] = systemServerCpuTimesUs[i] * binderThreadCpuTimesUs[i]
+ / systemServerThreadCpuTimesUs[i];
+ }
+ return results;
}
/**
@@ -14181,18 +14197,14 @@
updateSystemServiceCallStats();
if (mSystemServerThreadCpuTimesUs != null) {
pw.println("Per UID System server binder time in ms:");
+ long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
for (int i = 0; i < size; i++) {
int u = mUidStats.keyAt(i);
Uid uid = mUidStats.get(u);
double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
-
long timeUs = 0;
- for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) {
- int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length;
- for (int speed = 0; speed < numSpeeds; speed++) {
- timeUs += getSystemServiceTimeAtCpuSpeed(cluster, speed)
- * proportionalSystemServiceUsage;
- }
+ for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) {
+ timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage;
}
pw.print(" ");
@@ -15728,8 +15740,10 @@
mUidStats.append(uid, u);
}
- mSystemServerThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
- mBinderThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
+ mSystemServerCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
+ mSystemServerThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in,
+ mOnBatteryTimeBase);
+ mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
}
public void writeToParcel(Parcel out, int flags) {
@@ -15778,7 +15792,7 @@
mScreenOnTimer.writeToParcel(out, uSecRealtime);
mScreenDozeTimer.writeToParcel(out, uSecRealtime);
- for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+ for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
}
mInteractiveTimer.writeToParcel(out, uSecRealtime);
@@ -15794,7 +15808,7 @@
mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
mPhoneSignalScanningTimer.writeToParcel(out, uSecRealtime);
- for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
+ for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; i++) {
mPhoneDataConnectionsTimer[i].writeToParcel(out, uSecRealtime);
}
for (int i = 0; i < NUM_NETWORK_ACTIVITY_TYPES; i++) {
@@ -15809,18 +15823,18 @@
mWifiMulticastWakelockTimer.writeToParcel(out, uSecRealtime);
mWifiOnTimer.writeToParcel(out, uSecRealtime);
mGlobalWifiRunningTimer.writeToParcel(out, uSecRealtime);
- for (int i=0; i<NUM_WIFI_STATES; i++) {
+ for (int i = 0; i < NUM_WIFI_STATES; i++) {
mWifiStateTimer[i].writeToParcel(out, uSecRealtime);
}
- for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+ for (int i = 0; i < NUM_WIFI_SUPPL_STATES; i++) {
mWifiSupplStateTimer[i].writeToParcel(out, uSecRealtime);
}
- for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+ for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
}
mWifiActiveTimer.writeToParcel(out, uSecRealtime);
mWifiActivity.writeToParcel(out, 0);
- for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime);
}
mBluetoothActivity.writeToParcel(out, 0);
@@ -15930,8 +15944,9 @@
} else {
out.writeInt(0);
}
- writeCpuSpeedCountersToParcel(out, mSystemServerThreadCpuTimesUs);
- writeCpuSpeedCountersToParcel(out, mBinderThreadCpuTimesUs);
+ LongSamplingCounterArray.writeToParcel(out, mSystemServerCpuTimesUs);
+ LongSamplingCounterArray.writeToParcel(out, mSystemServerThreadCpuTimesUs);
+ LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs);
}
private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) {
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
new file mode 100644
index 0000000..0578b89
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -0,0 +1,259 @@
+/*
+ * 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.internal.os;
+
+import static android.os.Process.PROC_OUT_LONG;
+import static android.os.Process.PROC_SPACE_TERM;
+
+import android.annotation.Nullable;
+import android.os.Process;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Iterates over all threads owned by a given process, and return the CPU usage for
+ * each thread. The CPU usage statistics contain the amount of time spent in a frequency band. CPU
+ * usage is collected using {@link ProcTimeInStateReader}.
+ */
+public class KernelSingleProcessCpuThreadReader {
+
+ private static final String TAG = "KernelSingleProcCpuThreadRdr";
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * The name of the file to read CPU statistics from, must be found in {@code
+ * /proc/$PID/task/$TID}
+ */
+ private static final String CPU_STATISTICS_FILENAME = "time_in_state";
+
+ private static final String PROC_STAT_FILENAME = "stat";
+
+ /** Directory under /proc/$PID containing CPU stats files for threads */
+ public static final String THREAD_CPU_STATS_DIRECTORY = "task";
+
+ /** Default mount location of the {@code proc} filesystem */
+ private static final Path DEFAULT_PROC_PATH = Paths.get("/proc");
+
+ /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */
+ private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state");
+
+ /** See https://man7.org/linux/man-pages/man5/proc.5.html */
+ private static final int[] PROCESS_FULL_STATS_FORMAT = new int[]{
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM,
+ PROC_SPACE_TERM | PROC_OUT_LONG, // 14: utime
+ PROC_SPACE_TERM | PROC_OUT_LONG, // 15: stime
+ // Ignore remaining fields
+ };
+
+ private final long[] mProcessFullStatsData = new long[2];
+
+ private static final int PROCESS_FULL_STAT_UTIME = 0;
+ private static final int PROCESS_FULL_STAT_STIME = 1;
+
+ /** Used to read and parse {@code time_in_state} files */
+ private final ProcTimeInStateReader mProcTimeInStateReader;
+
+ private final int mPid;
+
+ /** Where the proc filesystem is mounted */
+ private final Path mProcPath;
+
+ // How long a CPU jiffy is in milliseconds.
+ private final long mJiffyMillis;
+
+ // Path: /proc/<pid>/stat
+ private final String mProcessStatFilePath;
+
+ // Path: /proc/<pid>/task
+ private final Path mThreadsDirectoryPath;
+
+ /**
+ * Count of frequencies read from the {@code time_in_state} file. Read from {@link
+ * #mProcTimeInStateReader#getCpuFrequenciesKhz()}.
+ */
+ private int mFrequencyCount;
+
+ /**
+ * Create with a path where `proc` is mounted. Used primarily for testing
+ *
+ * @param pid PID of the process whose threads are to be read.
+ * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc})
+ */
+ @VisibleForTesting
+ public KernelSingleProcessCpuThreadReader(
+ int pid,
+ Path procPath) throws IOException {
+ mPid = pid;
+ mProcPath = procPath;
+ mProcTimeInStateReader = new ProcTimeInStateReader(
+ mProcPath.resolve(INITIAL_TIME_IN_STATE_PATH));
+ long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
+ mJiffyMillis = 1000 / jiffyHz;
+ mProcessStatFilePath =
+ mProcPath.resolve(String.valueOf(mPid)).resolve(PROC_STAT_FILENAME).toString();
+ mThreadsDirectoryPath =
+ mProcPath.resolve(String.valueOf(mPid)).resolve(THREAD_CPU_STATS_DIRECTORY);
+ }
+
+ /**
+ * Create the reader and handle exceptions during creation
+ *
+ * @return the reader, null if an exception was thrown during creation
+ */
+ @Nullable
+ public static KernelSingleProcessCpuThreadReader create(int pid) {
+ try {
+ return new KernelSingleProcessCpuThreadReader(pid, DEFAULT_PROC_PATH);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to initialize KernelSingleProcessCpuThreadReader", e);
+ return null;
+ }
+ }
+
+ /**
+ * Get the CPU frequencies that correspond to the times reported in {@link
+ * ThreadCpuUsage#usageTimesMillis}
+ */
+ public int getCpuFrequencyCount() {
+ if (mFrequencyCount == 0) {
+ mFrequencyCount = mProcTimeInStateReader.getFrequenciesKhz().length;
+ }
+ return mFrequencyCount;
+ }
+
+ /**
+ * Get the total and per-thread CPU usage of the process with the PID specified in the
+ * constructor.
+ */
+ @Nullable
+ public ProcessCpuUsage getProcessCpuUsage() {
+ if (DEBUG) {
+ Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID "
+ + mPid);
+ }
+
+ if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null,
+ mProcessFullStatsData, null)) {
+ Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath);
+ return null;
+ }
+
+ long utime = mProcessFullStatsData[PROCESS_FULL_STAT_UTIME];
+ long stime = mProcessFullStatsData[PROCESS_FULL_STAT_STIME];
+
+ long processCpuTimeMillis = (utime + stime) * mJiffyMillis;
+
+ final ArrayList<ThreadCpuUsage> threadCpuUsages = new ArrayList<>();
+ try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) {
+ for (Path threadDirectory : threadPaths) {
+ ThreadCpuUsage threadCpuUsage = getThreadCpuUsage(threadDirectory);
+ if (threadCpuUsage == null) {
+ continue;
+ }
+ threadCpuUsages.add(threadCpuUsage);
+ }
+ } catch (IOException | DirectoryIteratorException e) {
+ // Expected when a process finishes
+ return null;
+ }
+
+ // If we found no threads, then the process has exited while we were reading from it
+ if (threadCpuUsages.isEmpty()) {
+ return null;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads");
+ }
+ return new ProcessCpuUsage(processCpuTimeMillis, threadCpuUsages);
+ }
+
+ /**
+ * Get a thread's CPU usage
+ *
+ * @param threadDirectory the {@code /proc} directory of the thread
+ * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
+ * removed while collecting information
+ */
+ @Nullable
+ private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
+ // Get the thread ID from the directory name
+ final int threadId;
+ try {
+ final String directoryName = threadDirectory.getFileName().toString();
+ threadId = Integer.parseInt(directoryName);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e);
+ return null;
+ }
+
+ // Get the CPU statistics from the directory
+ final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME);
+ final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath);
+ if (cpuUsages == null) {
+ return null;
+ }
+
+ return new ThreadCpuUsage(threadId, cpuUsages);
+ }
+
+ /** CPU usage of a process and all of its threads */
+ public static class ProcessCpuUsage {
+ public final long cpuTimeMillis;
+ public final List<ThreadCpuUsage> threadCpuUsages;
+
+ ProcessCpuUsage(long cpuTimeMillis, List<ThreadCpuUsage> threadCpuUsages) {
+ this.cpuTimeMillis = cpuTimeMillis;
+ this.threadCpuUsages = threadCpuUsages;
+ }
+ }
+
+ /** CPU usage of a thread */
+ public static class ThreadCpuUsage {
+ public final int threadId;
+ public final long[] usageTimesMillis;
+
+ ThreadCpuUsage(int threadId, long[] usageTimesMillis) {
+ this.threadId = threadId;
+ this.usageTimesMillis = usageTimesMillis;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index 3aa2390..d9f0dc0 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -16,25 +16,28 @@
package com.android.internal.os;
+import android.annotation.Nullable;
import android.os.Process;
import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
* Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
* by various threads of the System Server.
*/
public class SystemServerCpuThreadReader {
- private KernelCpuThreadReader mKernelCpuThreadReader;
+ private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
private int[] mBinderThreadNativeTids = new int[0]; // Sorted
- private int[] mThreadCpuTimesUs;
- private int[] mBinderThreadCpuTimesUs;
+ private long mProcessCpuTimeUs;
+ private long[] mThreadCpuTimesUs;
+ private long[] mBinderThreadCpuTimesUs;
+ private long mLastProcessCpuTimeUs;
private long[] mLastThreadCpuTimesUs;
private long[] mLastBinderThreadCpuTimesUs;
@@ -42,6 +45,8 @@
* Times (in microseconds) spent by the system server UID.
*/
public static class SystemServiceCpuThreadTimes {
+ // The entire process
+ public long processCpuTimeUs;
// All threads
public long[] threadCpuTimesUs;
// Just the threads handling incoming binder calls
@@ -55,22 +60,16 @@
*/
public static SystemServerCpuThreadReader create() {
return new SystemServerCpuThreadReader(
- KernelCpuThreadReader.create(0, uid -> uid == Process.myUid()));
+ KernelSingleProcessCpuThreadReader.create(Process.myPid()));
}
@VisibleForTesting
- public SystemServerCpuThreadReader(Path procPath, int systemServerUid) throws IOException {
- this(new KernelCpuThreadReader(0, uid -> uid == systemServerUid, null, null,
- new KernelCpuThreadReader.Injector() {
- @Override
- public int getUidForPid(int pid) {
- return systemServerUid;
- }
- }));
+ public SystemServerCpuThreadReader(Path procPath, int pid) throws IOException {
+ this(new KernelSingleProcessCpuThreadReader(pid, procPath));
}
@VisibleForTesting
- public SystemServerCpuThreadReader(KernelCpuThreadReader kernelCpuThreadReader) {
+ public SystemServerCpuThreadReader(KernelSingleProcessCpuThreadReader kernelCpuThreadReader) {
mKernelCpuThreadReader = kernelCpuThreadReader;
}
@@ -82,11 +81,12 @@
/**
* Returns delta of CPU times, per thread, since the previous call to this method.
*/
+ @Nullable
public SystemServiceCpuThreadTimes readDelta() {
+ int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
if (mBinderThreadCpuTimesUs == null) {
- int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz().length;
- mThreadCpuTimesUs = new int[numCpuFrequencies];
- mBinderThreadCpuTimesUs = new int[numCpuFrequencies];
+ mThreadCpuTimesUs = new long[numCpuFrequencies];
+ mBinderThreadCpuTimesUs = new long[numCpuFrequencies];
mLastThreadCpuTimesUs = new long[numCpuFrequencies];
mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
@@ -95,49 +95,47 @@
mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
}
+ mProcessCpuTimeUs = 0;
Arrays.fill(mThreadCpuTimesUs, 0);
Arrays.fill(mBinderThreadCpuTimesUs, 0);
- ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsage =
+ KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
mKernelCpuThreadReader.getProcessCpuUsage();
- int processCpuUsageSize = processCpuUsage.size();
- for (int i = 0; i < processCpuUsageSize; i++) {
- KernelCpuThreadReader.ProcessCpuUsage pcu = processCpuUsage.get(i);
- ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = pcu.threadCpuUsages;
- if (threadCpuUsages != null) {
- int threadCpuUsagesSize = threadCpuUsages.size();
- for (int j = 0; j < threadCpuUsagesSize; j++) {
- KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j);
- boolean isBinderThread =
- Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0;
+ if (processCpuUsage == null) {
+ return null;
+ }
- final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length);
- for (int k = 0; k < len; k++) {
- int usageTimeUs = tcu.usageTimesMillis[k] * 1000;
- mThreadCpuTimesUs[k] += usageTimeUs;
- if (isBinderThread) {
- mBinderThreadCpuTimesUs[k] += usageTimeUs;
- }
- }
+ mProcessCpuTimeUs = processCpuUsage.cpuTimeMillis * 1000;
+
+ List<KernelSingleProcessCpuThreadReader.ThreadCpuUsage> threadCpuUsages =
+ processCpuUsage.threadCpuUsages;
+ int threadCpuUsagesSize = threadCpuUsages.size();
+ for (int i = 0; i < threadCpuUsagesSize; i++) {
+ KernelSingleProcessCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(i);
+ boolean isBinderThread =
+ Arrays.binarySearch(mBinderThreadNativeTids, tcu.threadId) >= 0;
+ for (int k = 0; k < numCpuFrequencies; k++) {
+ long usageTimeUs = tcu.usageTimesMillis[k] * 1000;
+ mThreadCpuTimesUs[k] += usageTimeUs;
+ if (isBinderThread) {
+ mBinderThreadCpuTimesUs[k] += usageTimeUs;
}
}
}
for (int i = 0; i < mThreadCpuTimesUs.length; i++) {
- if (mThreadCpuTimesUs[i] < mLastThreadCpuTimesUs[i]) {
- mDeltaCpuThreadTimes.threadCpuTimesUs[i] = mThreadCpuTimesUs[i];
- mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
- } else {
- mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
- mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i];
- mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
- mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i];
- }
+ mDeltaCpuThreadTimes.processCpuTimeUs =
+ Math.max(0, mProcessCpuTimeUs - mLastProcessCpuTimeUs);
+ mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
+ Math.max(0, mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i]);
+ mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
+ Math.max(0, mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i]);
mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i];
mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
}
+ mLastProcessCpuTimeUs = mProcessCpuTimeUs;
+
return mDeltaCpuThreadTimes;
}
-
}
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index 481b901..fc36e50 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -34,7 +34,9 @@
private final PowerProfile mPowerProfile;
private final BatteryStats mBatteryStats;
// Tracks system server CPU [cluster][speed] power in milliAmp-microseconds
- private double[][] mSystemServicePowerMaUs;
+ // Data organized like this:
+ // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
+ private double[] mSystemServicePowerMaUs;
public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) {
mPowerProfile = powerProfile;
@@ -50,37 +52,41 @@
updateSystemServicePower();
}
- double cpuPowerMaUs = 0;
- int numCpuClusters = mPowerProfile.getNumCpuClusters();
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
- for (int speed = 0; speed < numSpeeds; speed++) {
- cpuPowerMaUs += mSystemServicePowerMaUs[cluster][speed] * proportionalUsage;
+ if (mSystemServicePowerMaUs != null) {
+ double cpuPowerMaUs = 0;
+ for (int i = 0; i < mSystemServicePowerMaUs.length; i++) {
+ cpuPowerMaUs += mSystemServicePowerMaUs[i] * proportionalUsage;
}
- }
- app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
+ app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
+ }
}
}
private void updateSystemServicePower() {
+ final long[] systemServiceTimeAtCpuSpeeds = mBatteryStats.getSystemServiceTimeAtCpuSpeeds();
+ if (systemServiceTimeAtCpuSpeeds == null) {
+ return;
+ }
+
+ if (mSystemServicePowerMaUs == null) {
+ mSystemServicePowerMaUs = new double[systemServiceTimeAtCpuSpeeds.length];
+ }
+ int index = 0;
final int numCpuClusters = mPowerProfile.getNumCpuClusters();
- mSystemServicePowerMaUs = new double[numCpuClusters][];
for (int cluster = 0; cluster < numCpuClusters; cluster++) {
final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
- mSystemServicePowerMaUs[cluster] = new double[numSpeeds];
for (int speed = 0; speed < numSpeeds; speed++) {
- mSystemServicePowerMaUs[cluster][speed] =
- mBatteryStats.getSystemServiceTimeAtCpuSpeed(cluster, speed)
+ mSystemServicePowerMaUs[index] =
+ systemServiceTimeAtCpuSpeeds[index]
* mPowerProfile.getAveragePowerForCpuCore(cluster, speed);
+ index++;
}
}
+
if (DEBUG) {
- Log.d(TAG, "System service power per CPU cluster and frequency");
- for (int cluster = 0; cluster < numCpuClusters; cluster++) {
- Log.d(TAG, "Cluster[" + cluster + "]: "
- + Arrays.toString(mSystemServicePowerMaUs[cluster]));
- }
+ Log.d(TAG, "System service power per CPU cluster and frequency:"
+ + Arrays.toString(mSystemServicePowerMaUs));
}
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index d5f54a1..fff9ac9 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -43,7 +43,6 @@
public BaseIWindow() {}
private IWindowSession mSession;
- public int mSeq;
public void setSession(IWindowSession session) {
mSession = session;
@@ -140,12 +139,6 @@
}
@Override
- public void dispatchSystemUiVisibilityChanged(int seq, int globalUi,
- int localValue, int localChanges) {
- mSeq = seq;
- }
-
- @Override
public void dispatchWallpaperCommand(String action, int x, int y,
int z, Bundle extras, boolean sync) {
if (sync) {
diff --git a/core/java/com/android/internal/view/IInputMethodClient.aidl b/core/java/com/android/internal/view/IInputMethodClient.aidl
index 4509032..c9443b0 100644
--- a/core/java/com/android/internal/view/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/view/IInputMethodClient.aidl
@@ -33,4 +33,5 @@
void reportPreRendered(in EditorInfo info);
void applyImeVisibility(boolean setVisible);
void updateActivityViewToScreenMatrix(int bindSequence, in float[] matrixValues);
+ void setImeTraceEnabled(boolean enabled);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index a1cbd3f..5a06273 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -77,4 +77,6 @@
void removeImeSurface();
/** Remove the IME surface. Requires passing the currently focused window. */
void removeImeSurfaceFromWindow(in IBinder windowToken);
+ void startProtoDump(in byte[] clientProtoDump);
+ boolean isImeTraceEnabled();
}
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index ddee81a6..ff3543c8 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -17,9 +17,6 @@
package com.android.internal.widget;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.method.KeyListener;
@@ -30,8 +27,6 @@
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputContentInfo;
-import android.widget.RichContentReceiver;
import android.widget.TextView;
public class EditableInputConnection extends BaseInputConnection {
@@ -186,28 +181,6 @@
}
@Override
- public boolean commitContent(InputContentInfo content, int flags, Bundle opts) {
- int targetSdkVersion = mTextView.getContext().getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion <= Build.VERSION_CODES.R) {
- return false;
- }
-
- final ClipDescription description = content.getDescription();
- final RichContentReceiver<TextView> receiver = mTextView.getRichContentReceiver();
- if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
- try {
- content.requestPermission();
- } catch (Exception e) {
- // TODO(b/147299828): Can we catch SecurityException instead?
- Log.w(TAG, "Can't insert content from IME; requestPermission() failed: " + e);
- return false; // Can't insert the content if we don't have permission to read it
- }
- }
- ClipData clip = new ClipData(description, new ClipData.Item(content.getContentUri()));
- return receiver.onReceive(mTextView, clip, RichContentReceiver.SOURCE_INPUT_METHOD, 0);
- }
-
- @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode);
diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h
index d629e0d..013c65f 100644
--- a/core/jni/core_jni_helpers.h
+++ b/core/jni/core_jni_helpers.h
@@ -90,6 +90,12 @@
return res;
}
+static inline jobject jniGetReferent(JNIEnv* env, jobject ref) {
+ jclass cls = FindClassOrDie(env, "java/lang/ref/Reference");
+ jmethodID get = GetMethodIDOrDie(env, cls, "get", "()Ljava/lang/Object;");
+ return env->CallObjectMethod(ref, get);
+}
+
/**
* Read the specified field from jobject, and convert to std::string.
* If the field cannot be obtained, return defaultValue.
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 11f6a91..542d26f 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -17,6 +17,9 @@
roosa@google.com
per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+# Biometrics
+kchyn@google.com
+
# Launcher
hyunyoungs@google.com
diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto
index 59556c4..c465233 100644
--- a/core/proto/android/server/peopleservice.proto
+++ b/core/proto/android/server/peopleservice.proto
@@ -46,6 +46,10 @@
// The notification channel id of the conversation.
optional string notification_channel_id = 4 [(.android.privacy).dest = DEST_EXPLICIT];
+ // The parent notification channel ID of the conversation. This is the notification channel where
+ // the notifications are posted before this conversation is customized by the user.
+ optional string parent_notification_channel_id = 8 [(.android.privacy).dest = DEST_EXPLICIT];
+
// Integer representation of shortcut bit flags.
optional int32 shortcut_flags = 5;
@@ -54,6 +58,11 @@
// The phone number of the contact.
optional string contact_phone_number = 7 [(.android.privacy).dest = DEST_EXPLICIT];
+
+ // The timestamp of the last event in millis.
+ optional int64 last_event_timestamp = 9;
+
+ // Next tag: 10
}
// On disk data of events.
diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto
index f14e3ed..b56bd2b 100644
--- a/core/proto/android/telephony/enums.proto
+++ b/core/proto/android/telephony/enums.proto
@@ -159,3 +159,50 @@
*/
SIM_STATE_PRESENT = 11;
}
+
+// Format of SMS message
+enum SmsFormatEnum {
+ /** Unknown format */
+ SMS_FORMAT_UNKNOWN = 0;
+ /** Format compliant with 3GPP TS 23.040 */
+ SMS_FORMAT_3GPP = 1;
+ /** Format compliant with 3GPP2 TS C.S0015-B */
+ SMS_FORMAT_3GPP2 = 2;
+}
+
+// Technology used to carry an SMS message
+enum SmsTechEnum {
+ /**
+ * Unknown SMS technology used to carry the SMS.
+ * This value is also used for injected SMS.
+ */
+ SMS_TECH_UNKNOWN = 0;
+ /** The SMS was carried over CS bearer in 3GPP network */
+ SMS_TECH_CS_3GPP = 1;
+ /** The SMS was carried over CS bearer in 3GPP2 network */
+ SMS_TECH_CS_3GPP2 = 2;
+ /** The SMS was carried over IMS */
+ SMS_TECH_IMS = 3;
+}
+
+// Types of SMS message
+enum SmsTypeEnum {
+ /** Normal type. */
+ SMS_TYPE_NORMAL = 0;
+ /** SMS-PP (point-to-point). */
+ SMS_TYPE_SMS_PP = 1;
+ /** Voicemail indication. */
+ SMS_TYPE_VOICEMAIL_INDICATION = 2;
+ /** Type 0 message (3GPP TS 23.040 9.2.3.9). */
+ SMS_TYPE_ZERO = 3;
+ /** WAP-PUSH message. */
+ SMS_TYPE_WAP_PUSH = 4;
+}
+
+// SMS errors
+enum SmsIncomingErrorEnum {
+ SMS_SUCCESS = 0;
+ SMS_ERROR_GENERIC = 1;
+ SMS_ERROR_NO_MEMORY = 2;
+ SMS_ERROR_NOT_SUPPORTED = 3;
+}
diff --git a/core/proto/android/view/imeinsetssourceconsumer.proto b/core/proto/android/view/imeinsetssourceconsumer.proto
index 6809163..5bee81b 100644
--- a/core/proto/android/view/imeinsetssourceconsumer.proto
+++ b/core/proto/android/view/imeinsetssourceconsumer.proto
@@ -17,6 +17,7 @@
syntax = "proto2";
import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
+import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto";
package android.view;
@@ -26,6 +27,7 @@
* Represents a {@link android.view.ImeInsetsSourceConsumer} object.
*/
message ImeInsetsSourceConsumerProto {
- optional .android.view.inputmethod.EditorInfoProto focused_editor = 1;
- optional bool is_requested_visible_awaiting_control = 2;
+ optional InsetsSourceConsumerProto insets_source_consumer = 1;
+ optional .android.view.inputmethod.EditorInfoProto focused_editor = 2;
+ optional bool is_requested_visible_awaiting_control = 3;
}
\ No newline at end of file
diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
index 7322139..f31d35b 100644
--- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
+++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
@@ -22,7 +22,6 @@
import "frameworks/base/core/proto/android/view/inputmethod/inputmethodmanager.proto";
import "frameworks/base/core/proto/android/view/viewrootimpl.proto";
import "frameworks/base/core/proto/android/view/insetscontroller.proto";
-import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto";
import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto";
import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
import "frameworks/base/core/proto/android/view/imefocuscontroller.proto";
@@ -54,14 +53,19 @@
/* required: elapsed realtime in nanos since boot of when this entry was logged */
optional fixed64 elapsed_realtime_nanos = 1;
- optional ClientSideProto client_side_dump = 2;
+ optional ClientsProto clients = 2;
+
+ // this wrapper helps to simplify the dumping logic
+ message ClientsProto {
+ repeated ClientSideProto client = 1;
+ }
/* groups together the dump from ime related client side classes */
message ClientSideProto {
- optional InputMethodManagerProto input_method_manager = 1;
- optional ViewRootImplProto view_root_impl = 2;
- optional InsetsControllerProto insets_controller = 3;
- optional InsetsSourceConsumerProto insets_source_consumer = 4;
+ optional int32 display_id = 1;
+ optional InputMethodManagerProto input_method_manager = 2;
+ optional ViewRootImplProto view_root_impl = 3;
+ optional InsetsControllerProto insets_controller = 4;
optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5;
optional EditorInfoProto editor_info = 6;
optional ImeFocusControllerProto ime_focus_controller = 7;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 34543f1..723cceb 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4322,6 +4322,10 @@
<permission android:name="android.permission.WRITE_DREAM_STATE"
android:protectionLevel="signature|privileged" />
+ <!-- @hide Allows applications to read whether ambient display is suppressed. -->
+ <permission android:name="android.permission.READ_DREAM_SUPPRESSION"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allow an application to read and write the cache partition.
@hide -->
<permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index b54dfc0..58d6a86 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Verhoog volume bo aanbevole vlak?\n\nOm lang tydperke teen hoë volume te luister, kan jou gehoor beskadig."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gebruik toeganklikheidkortpad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wanneer die kortpad aan is, sal \'n toeganklikheidkenmerk begin word as albei volumeknoppies 3 sekondes lank gedruk word."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Skakel toeganklikheidkenmerke aan?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"As jy albei volumesleutels vir \'n paar sekondes hou, skakel dit toeganklikheidkenmerke aan. Dit kan verander hoe jou toestel werk.\n\nHuidige kenmerke:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nJy kan geselekteerde kenmerke in Instellings en Toeganklikheid verander."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Skakel <xliff:g id="SERVICE">%1$s</xliff:g> aan?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"As jy albei volumesleutels vir \'n paar sekondes hou, skakel dit <xliff:g id="SERVICE">%1$s</xliff:g>, \'n toeganklikheidkenmerk, aan. Dit kan verander hoe jou toestel werk.\n\nJy kan hierdie kortpad na \'n ander kenmerk in Instellings en Toeganklikheid verander."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Skakel aan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Moenie aanskakel nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 668ce42..b3878d8 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ድምጹ ከሚመከረው መጠን በላይ ከፍ ይበል?\n\nበከፍተኛ ድምጽ ለረጅም ጊዜ ማዳመጥ ጆሮዎን ሊጎዳው ይችላል።"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"የተደራሽነት አቋራጭ ጥቅም ላይ ይዋል?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"አቋራጩ ሲበራ ሁለቱንም የድምጽ አዝራሮች ለ3 ሰከንዶች ተጭኖ መቆየት የተደራሽነት ባህሪን ያስጀምረዋል።"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"የተደራሽነት ባሕሪያት ይብሩ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ሁለቱንም የድምፅ ቁልፎች ወደ ታች ለጥቂት ሰከንዶች መያዝ የተደራሽነት ባሕሪያትን ያበራል። ይህ የእርስዎ መሣሪያ እንዴት እንደሚሠራ ሊለውጥ ይችላል።\n\nየአሁን ባሕሪያት፦\n<xliff:g id="SERVICE">%1$s</xliff:g>\nበቅንብሮች > ተደራሽነት ውስጥ የተመረጡትን ባሕሪያት መለወጥ ይችላሉ።"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ይብራ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ሁለቱንም የድምፅ ቁልፎች ወደ ታች ለጥቂት ሰከንዶች መያዝ የተደራሽነት ባሕሪያትን <xliff:g id="SERVICE">%1$s</xliff:g> ያበራል። ይህ የእርስዎ መሣሪያ እንዴት እንደሚሠራ ሊለውጥ ይችላል።\n\nበቅንብሮች > ተደራሽነት ውስጥ ወደ ሌላ ባሕሪ ይህን አቋራጭ መለወጥ ይችላሉ።"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"አብራ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"አታብራ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index fdb351c..44055e1 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1710,10 +1710,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"هل تريد رفع مستوى الصوت فوق المستوى الموصى به؟\n\nقد يضر سماع صوت عالٍ لفترات طويلة بسمعك."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"هل تريد استخدام اختصار \"سهولة الاستخدام\"؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"عند تفعيل الاختصار، يؤدي الضغط على زرّي التحكّم في مستوى الصوت معًا لمدة 3 ثوانٍ إلى تفعيل إحدى ميزات إمكانية الوصول."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"هل تريد تفعيل ميزات إمكانية الوصول؟"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"يؤدي الضغط مع الاستمرار على كلا مفتاحَي التحكّم في مستوى الصوت لبضع ثوانٍ إلى تفعيل ميزات إمكانية الوصول. قد يؤدي هذا الإجراء إلى تغيير طريقة عمل جهازك.\n\nالميزات الحالية:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nيمكنك تغيير الميزات المحددة في الإعدادات > إمكانية الوصول."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"هل تريد تفعيل <xliff:g id="SERVICE">%1$s</xliff:g>؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"يؤدي الضغط مع الاستمرار لبضع ثوانٍ على كلا مفتاحَي التحكّم في مستوى الصوت إلى تفعيل <xliff:g id="SERVICE">%1$s</xliff:g> وهي إحدى ميزات إمكانية الوصول. يمكن أن يؤدي هذا الإجراء إلى تغيير كيفية عمل جهازك.\n\nيمكنك تغيير هذا الاختصار لاستخدامه مع ميزة أخرى في الإعدادات > أدوات تمكين الوصول."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"تفعيل"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"عدم التفعيل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 93ce15f..e787f23 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"অনুমোদিত স্তৰতকৈ ওপৰলৈ ভলিউম বঢ়াব নেকি?\n\nদীৰ্ঘ সময়ৰ বাবে উচ্চ ভলিউমত শুনাৰ ফলত শ্ৰৱণ ক্ষমতাৰ ক্ষতি হ\'ব পাৰে।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"দিব্যাংগসকলৰ সুবিধাৰ শ্বৰ্টকাট ব্যৱহাৰ কৰেনে?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"শ্বৰ্টকাটটো অন হৈ থকাৰ সময়ত দুয়োটা ভলিউম বুটাম ৩ ছেকেণ্ডৰ বাবে হেঁচি ধৰি ৰাখিলে এটা সাধ্য সুবিধা আৰম্ভ হ’ব।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"সাধ্য-সুবিধাসমূহ অন কৰিবনে?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে সাধ্য-সুবিধাসমূহ অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nবর্তমানৰ সুবিধাসমূহ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাত কিছুমান নিৰ্দিষ্ট সুবিধা সলনি কৰিব পাৰে।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> অন কৰিবনে?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"দুয়োটা ভলিউম কী কিছুসময়ৰ বাবে ধৰি থাকিলে এটা সাধ্য- সুবিধা <xliff:g id="SERVICE">%1$s</xliff:g> অন কৰে। এইটোৱে আপোনাৰ ডিভাইচটোৱে কাম কৰাৰ ধৰণ সলনি কৰিব পাৰে।\n\nআপুনি ছেটিংসমূহ > সাধ্য-সুবিধাসমূহত এই শ্বৰ্টকাটটো অন্য এটা সুবিধালৈ সলনি কৰিব পাৰে।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"অন কৰক"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"অন নকৰিব"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 01b815b..af57cf9 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Səsin həcmi tövsiyə olunan səviyyədən artıq olsun?\n\nYüksək səsi uzun zaman dinləmək eşitmə qabiliyyətinizə zərər vura bilər."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Əlçatımlılıq Qısayolu istifadə edilsin?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Qısayol aktiv olduqda, hər iki səs düyməsinə 3 saniyə basıb saxlamaqla əlçatımlılıq funksiyası başladılacaq."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Əlçatımlılıq funksiyaları aktiv edilsin?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyaları aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nCari funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAyarlar və Əlçatımlılıq bölməsində seçilmiş funksiyaları dəyişə bilərsiniz."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktiv edilsin?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hər iki səs səviyyəsi düyməsinə bir neçə saniyə basıb saxladıqda əlçatımlılıq funksiyası olan <xliff:g id="SERVICE">%1$s</xliff:g> aktiv olur. Bu, cihazınızın işləmə qaydasını dəyişə bilər.\n\nAyarlar və Əlçatımlılıq bölməsində bu qısayolu başqa bir funksiyata dəyişə bilərsiniz."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktiv edin"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktiv etməyin"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index e2fc8f1..465e9d2 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite da pojačate zvuk iznad preporučenog nivoa?\n\nSlušanje glasne muzike duže vreme može da vam ošteti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li da koristite prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kada je prečica uključena, pritisnite oba dugmeta za jačinu zvuka da biste pokrenuli funkciju pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite li da uključite funkcije pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako zadržite oba tastera za jačinu zvuka par sekundi, uključiće se funkcije pristupačnosti. To može da promeni način rada uređaja.\n\nPostojeće funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nMožete da promenite izabrane funkcije u odeljku Podešavanja > Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite li da uključite uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako zadržite oba tastera za jačinu zvuka par sekundi, uključuje se <xliff:g id="SERVICE">%1$s</xliff:g>, funkcija pristupačnosti. To može da promeni način rada uređaja.\n\nMožete da promenite funkciju na koju se odnosi ova prečica u odeljku Podešavanja > Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne uključuj"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 45008f2..f0594a1 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Павялiчыць гук вышэй рэкамендаванага ўзроўню?\n\nДоўгае праслухоўванне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Выкарыстоўваць камбінацыю хуткага доступу для спецыяльных магчымасцей?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Калі хуткі доступ уключаны, вы можаце націснуць абедзве кнопкі гучнасці і ўтрымліваць іх 3 секунды, каб запусціць функцыю спецыяльных магчымасцей."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Уключыць спецыяльныя магчымасці?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае спецыяльныя магчымасці. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nБягучыя функцыі:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nВыбраныя функцыі можна змяніць у меню \"Налады > Спецыяльныя магчымасці\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Уключыць службу \"<xliff:g id="SERVICE">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Утрымліванне націснутымі абедзвюх клавіш гучнасці на працягу некалькіх секунд уключае службу \"<xliff:g id="SERVICE">%1$s</xliff:g>\", якая з\'яўляецца спецыяльнай магчымасцю. У выніку ваша прылада можа пачаць працаваць па-іншаму.\n\nВы можаце задаць гэта спалучэнне клавіш для іншай функцыі ў меню \"Налады > Спецыяльныя магчымасці\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Уключыць"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не ўключаць"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 0c2c022..99f6072 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Да се увеличи ли силата на звука над препоръчителното ниво?\n\nПродължителното слушане при висока сила на звука може да увреди слуха ви."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Искате ли да използвате пряк път към функцията за достъпност?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Когато прекият път е включен, можете да стартирате дадена функция за достъпност, като натиснете двата бутона за силата на звука и ги задържите за 3 секунди."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Включване на функциите за достъпност?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Натиснете двата бутона за силата на звука и ги задръжте за няколко секунди, за да включите функциите за достъпност. Това може да промени начина, по който работи устройството ви.\n\nТекущи функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМожете да промените избраните функции от „Настройки“ > „Достъпност“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Да се включи ли <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Натиснете двата бутона за силата на звука и ги задръжте за няколко секунди, за да включите функцията за достъпност <xliff:g id="SERVICE">%1$s</xliff:g>. Това може да промени начина, по който работи устройството ви.\n\nМожете да зададете друга функция за този пряк път от „Настройки“ > „Достъпност“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Включване"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Без включване"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index b10d8dc..a07a1fe 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"প্রস্তাবিত স্তরের চেয়ে বেশি উঁচুতে ভলিউম বাড়াবেন?\n\nউঁচু ভলিউমে বেশি সময় ধরে কিছু শুনলে আপনার শ্রবনশক্তির ক্ষতি হতে পারে।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"অ্যাক্সেসযোগ্যতা শর্টকাট ব্যবহার করবেন?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"শর্টকাট চালু করা থাকাকালীন দুটি ভলিউম বোতাম একসাথে ৩ সেকেন্ড টিপে ধরে রাখলে একটি অ্যাকসেসিবিলিটি ফিচার চালু হবে।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"অ্যাক্সেসিবিলিটি ফিচার চালু করতে চান?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে অ্যাক্সেসিবিলিটি ফিচার চালু হয়ে যাবে। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nবর্তমান ফিচার:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nসেটিংস > অ্যাক্সেসিবিলিটি বিকল্প থেকে আপনি বাছাই করা ফিচার পরিবর্তন করতে পারবেন।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> চালু করতে চান?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"উভয় ভলিউম কী কয়েক সেকেন্ড ধরে থাকলে <xliff:g id="SERVICE">%1$s</xliff:g> চালু হয়ে যাবে। এটি একটি অ্যাক্সেসিবিলিটি ফিচার। এর ফলে, আপনার ডিভাইস কীভাবে কাজ করবে সেটিতে পরিবর্তন হতে পারে।\n\nসেটিংস > অ্যাক্সেসিবিলিটি থেকে আপনি এই শর্টকাট পরিবর্তন করতে পারবেন।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"চালু করুন"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"চালু করবেন না"</string>
@@ -1829,8 +1831,7 @@
<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_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-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 17c0d1d..1292fc1 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučenog nivoa?\n\nDužim slušanjem glasnog zvuka možete oštetiti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li koristiti Prečicu za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kada je prečica uključena, pritiskom i držanjem oba dugmeta za jačinu zvuka u trajanju od 3 sekunde pokrenut će se funkcija pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Uključiti funkcije pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkcije pristupačnosti. Ovo može uticati na način rada uređaja.\n\nTrenutne funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nOdabrane funkcije možete promijeniti u odjeljku Postavke > Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Uključiti <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako nekoliko sekundi držite pritisnute obje tipke za jačinu zvuka, uključit ćete funkciju pristupačnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Ovo može promijeniti način rada uređaja.\n\nOvu prečicu možete zamijeniti drugom funkcijom u odjeljku Postavke > Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nemoj uključiti"</string>
@@ -1860,7 +1862,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_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 e0c709d..28d961b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -991,8 +991,8 @@
<string name="searchview_description_submit" msgid="6771060386117334686">"Envia la consulta"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"Cerca per veu"</string>
<string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Vols activar l\'exploració tàctil?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interactuar amb la tauleta."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interactuar amb el telèfon."</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb la tauleta."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vol activar l\'exploració tàctil. Quan l\'exploració per tàctil està activada, pots escoltar o veure les descripcions del contingut seleccionat o utilitzar gestos per interaccionar amb el telèfon."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"Fa 1 mes"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Fa més d\'1 mes"</string>
<plurals name="last_num_days" formatted="false" msgid="687443109145393632">
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vols apujar el volum per sobre del nivell recomanat?\n\nSi escoltes música a un volum alt durant períodes llargs, pots danyar-te l\'oïda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vols fer servir la drecera d\'accessibilitat?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Si la drecera està activada, prem els dos botons de volum durant 3 segons per iniciar una funció d\'accessibilitat."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vols activar les funcions d\'accessibilitat?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si mantens premudes les dues tecles de volum durant uns segons, s\'activaran les funcions d\'accessibilitat. Això podria canviar el funcionament del teu dispositiu.\n\nFuncions actuals:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPots canviar les funcions seleccionades a Configuració > Accessibilitat."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vols activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si mantens premudes les dues tecles de volum durant uns segons, la funció d\'accessibilitat <xliff:g id="SERVICE">%1$s</xliff:g> s\'activarà. Això podria canviar el funcionament del teu dispositiu.\n\nPots canviar la funció d\'aquesta drecera a Configuració > Accessibilitat."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activa"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activis"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 095ec69..89f679e 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zvýšit hlasitost nad doporučenou úroveň?\n\nDlouhodobý poslech hlasitého zvuku může poškodit sluch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Použít zkratku přístupnosti?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Když je tato zkratka zapnutá, můžete funkci přístupnosti spustit tím, že na tři sekundy podržíte obě tlačítka hlasitosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Zapnout funkce pro usnadnění přístupu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Podržením obou tlačítek hlasitosti po dobu několika sekund zapnete funkce pro usnadnění přístupu. Tato funkce může změnit fungování zařízení.\n\nAktuální funkce:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVybrané funkce můžete změnit v Nastavení > Přístupnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Zapnout <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Podržením obou tlačítek hlasitosti po dobu několika sekund zapnete funkci pro usnadnění přístupu <xliff:g id="SERVICE">%1$s</xliff:g>. Tato funkce může změnit fungování zařízení.\n\nZkratku můžete nastavit na jinou funkci v Nastavení > Přístupnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Zapnout"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nezapínat"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 499b9de..e9d477d 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vil du skrue højere op end det anbefalede lydstyrkeniveau?\n\nDu kan skade hørelsen ved at lytte til meget høj musik over længere tid."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vil du bruge genvejen til Hjælpefunktioner?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Når genvejen er aktiveret, kan du starte en hjælpefunktion ved at trykke på begge lydstyrkeknapper i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vil du aktivere hjælpefunktionerne?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hvis du holder begge lydstyrkeknapperne nede i et par sekunder, aktiveres hjælpefunktionerne. Det kan ændre på, hvordan din enhed fungerer.\n\nAktuelle funktioner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan ændre de valgte funktioner i Indstillinger > Hjælpefunktioner."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vil du aktivere <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hvis du holder begge lydstyrkeknapperne nede i et par sekunder, aktiveres hjælpefunktionen <xliff:g id="SERVICE">%1$s</xliff:g>. Det kan ændre på, hvordan din enhed fungerer.\n\nDu kan ændre denne genvej til en anden funktion i Indstillinger > Hjælpefunktioner."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivér"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktivér ikke"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 867efac..682491e 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lautstärke über den Schwellenwert anheben?\n\nWenn du über einen längeren Zeitraum Musik in hoher Lautstärke hörst, kann dies dein Gehör schädigen."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Verknüpfung für Bedienungshilfen verwenden?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Wenn die Verknüpfung aktiviert ist, kannst du die beiden Lautstärketasten drei Sekunden lang gedrückt halten, um eine Bedienungshilfe zu starten."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bedienungshilfen aktivieren?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfen. Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nAktuelle Funktionen:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kannst ausgewählte Funktionen unter \"Einstellungen\" > \"Bedienungshilfen\" ändern."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktivieren?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfe \"<xliff:g id="SERVICE">%1$s</xliff:g>\". Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nUnter \"Einstellungen > \"Bedienungshilfen\" kannst du dieser Verknüpfung eine andere Funktion zuweisen."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivieren"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nicht aktivieren"</string>
@@ -1792,8 +1794,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
- <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
- <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
+ <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string>
+ <string name="battery_saver_description" msgid="6794188153647295212">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string>
<string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string>
@@ -1829,8 +1831,7 @@
<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_next_day" msgid="1403042784161725038">"Bis <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
<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>
@@ -2002,9 +2003,9 @@
<string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geben"</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string>
- <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
- <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Energiesparmodus"</string>
- <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Energiesparmodus deaktiviert"</string>
+ <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Stromsparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string>
+ <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Stromsparmodus"</string>
+ <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Stromsparmodus deaktiviert"</string>
<string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Das Smartphone ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
<string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Das Tablet ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
<string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Das Gerät ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index d4d1c5a..9449ca0 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Αυξάνετε την ένταση ήχου πάνω από το επίπεδο ασφαλείας;\n\nΑν ακούτε μουσική σε υψηλή ένταση για μεγάλο χρονικό διάστημα ενδέχεται να προκληθεί βλάβη στην ακοή σας."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Να χρησιμοποιείται η συντόμευση προσβασιμότητας;"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Όταν η συντόμευση είναι ενεργοποιημένη, το πάτημα και των δύο κουμπιών έντασης ήχου για 3 δευτερόλεπτα θα ξεκινήσει μια λειτουργία προσβασιμότητας."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Θέλετε να ενεργοποιήσετε τις λειτουργίες προσβασιμότητας;"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Για να ενεργοποιήσετε τις λειτουργίες προσβασιμότητας, πατήστε παρατεταμένα τα δύο πλήκτρα έντασης για μερικά δευτερόλεπτα. Αυτό ενδέχεται να αλλάξει τον τρόπο λειτουργίας της συσκευής σας.\n\nΤρέχουσες λειτουργίες:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nΜπορείτε να αλλάξετε τις επιλεγμένες λειτουργίες στις Ρυθμίσεις > Προσβασιμότητα."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Θέλετε να ενεργοποιήσετε τη λειτουργία <xliff:g id="SERVICE">%1$s</xliff:g>;"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Μπορείτε να ενεργοποιήσετε τη λειτουργία <xliff:g id="SERVICE">%1$s</xliff:g>, η οποία είναι μία από τις λειτουργίες προσβασιμότητας, πατώντας παρατεταμένα ταυτόχρονα τα δύο πλήκτρα έντασης ήχου για μερικά δευτερόλεπτα. Αυτό ενδέχεται να αλλάξει τον τρόπο λειτουργίας της συσκευής σας.\n\nΜπορείτε να αλλάξετε αυτή τη συντόμευση σε μια άλλη λειτουργία στις Ρυθμίσεις > Προσβασιμότητα."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ενεργοποίηση"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Να μην ενεργοποιηθούν"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index f140ce3..0788a60 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"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">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%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>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index e814bdf..6e841ec 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"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">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%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>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 73f1562..731f184 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"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">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%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>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 122ea7f..8bd3597 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for three seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"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">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%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>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 53d8c66..f9793d2 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1622,10 +1622,10 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Raise volume above recommended level?\n\nListening at high volume for long periods may damage your hearing."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Use Accessibility Shortcut?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"When the shortcut is on, pressing both volume buttons for 3 seconds will start an accessibility feature."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Turn on accessibility features?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Turn on shortcut for accessibility features?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"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">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Turn on <xliff:g id="SERVICE">%1$s</xliff:g> shortcut?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Holding down both volume keys for a few seconds turns on <xliff:g id="SERVICE">%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>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Turn on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Don’t turn on"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 953b6fc..0353000 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar a un alto volumen durante largos períodos puede dañar tu audición."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Usar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Cuando la combinación de teclas está activada, puedes presionar los botones de volumen durante 3 segundos para iniciar una función de accesibilidad."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Quieres activar las funciones de accesibilidad?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si mantienes presionadas las dos teclas de volumen durante unos segundos, se activarán las funciones de accesibilidad. Esto puede cambiar el funcionamiento de tu dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Configuración > Accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"¿Quieres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si mantienes presionadas ambas teclas de volumen durante unos segundos, se activará la función de accesibilidad <xliff:g id="SERVICE">%1$s</xliff:g>. Esto podría cambiar la forma en que funciona tu dispositivo.\n\nPuedes cambiar este acceso directo a otra función en Configuración > Accesibilidad."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activar"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 465d2ec..e64f568 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"¿Quieres subir el volumen por encima del nivel recomendado?\n\nEscuchar sonidos fuertes durante mucho tiempo puede dañar los oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"¿Utilizar acceso directo de accesibilidad?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Si el acceso directo está activado, pulsa los dos botones de volumen durante 3 segundos para iniciar una función de accesibilidad."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"¿Activar funciones de accesibilidad?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Al mantener pulsadas las dos teclas de volumen durante unos segundos, se activan las funciones de accesibilidad, que pueden cambiar el funcionamiento del dispositivo.\n\nFunciones actuales:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuedes cambiar las funciones seleccionadas en Ajustes > Accesibilidad."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"¿Quieres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Al mantener pulsadas ambas teclas de volumen durante unos segundos se activa <xliff:g id="SERVICE">%1$s</xliff:g>, una función de accesibilidad. Esta función puede modificar el funcionamiento del dispositivo.\n\nPuedes asignar este acceso directo a otra función en Ajustes > Accesibilidad."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"No activar"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 6d2a4c0f..a3622d64 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Kas suurendada helitugevuse taset üle soovitatud taseme?\n\nPikaajaline valju helitugevusega kuulamine võib kuulmist kahjustada."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Kas kasutada juurdepääsetavuse otseteed?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kui otsetee on sisse lülitatud, käivitab mõlema helitugevuse nupu kolm sekundit all hoidmine juurdepääsetavuse funktsiooni."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Kas lülitada juurdepääsufunktsioonid sisse?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hoidke juurdepääsufunktsioonide sisselülitamiseks mõlemat helitugevuse klahvi mõni sekund all. See võib teie seadme tööviisi muuta.\n\nPraegused funktsioonid:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nValitud funktsioone saab muuta jaotises Seaded > Juurdepääsetavus."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Kas lülitada <xliff:g id="SERVICE">%1$s</xliff:g> sisse?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Kui hoiate mõlemat helitugevuse klahvi mõni sekund all, lülitatakse juurdepääsufunktsioon <xliff:g id="SERVICE">%1$s</xliff:g> sisse. See võib teie seadme tööviisi muuta.\n\nSelle otsetee saab asendada muu otseteega jaotises Seaded > Juurdepääsetavus."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Lülita sisse"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ära lülita sisse"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 7241b97..47d41f5 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Bolumena gomendatutako mailatik gora igo nahi duzu?\n\nMusika bolumen handian eta denbora luzez entzuteak entzumena kalte diezazuke."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erabilerraztasun-lasterbidea erabili nahi duzu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Lasterbidea aktibatuta dagoenean, bi bolumen-botoiak hiru segundoz sakatuta abiaraziko da erabilerraztasun-eginbidea."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Erabilerraztasun-eginbideak aktibatu nahi dituzu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Eduki sakatuta bolumen-botoiak segundo batzuez erabilerraztasun-eginbideak aktibatzeko. Hori eginez gero, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nUneko eginbideak:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nHautatutako eginbideak aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> aktibatu nahi duzu?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Eduki sakatuta bolumen-botoiak segundo batzuez <xliff:g id="SERVICE">%1$s</xliff:g> izeneko erabilerraztasun-eginbidea aktibatzeko. Honen bidez, baliteke zure mugikorraren funtzionamendua aldatzea.\n\nLasterbide hau beste eginbide batengatik aldatzeko, joan Ezarpenak > Erabilerraztasuna atalera."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktibatu"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ez aktibatu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 02f4728..807a7ae 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"میزان صدا را به بالاتر از حد توصیه شده افزایش میدهید؟\n\nگوش دادن به صداهای بلند برای مدت طولانی میتواند به شنواییتان آسیب وارد کند."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"از میانبر دسترسپذیری استفاده شود؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"وقتی میانبر روشن باشد، با فشار دادن هردو دکمه صدا بهمدت ۳ ثانیه ویژگی دسترسپذیری فعال میشود."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ویژگیهای دسترسپذیری روشن شود؟"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"با پایین نگه داشتن هردو کلید میزان صدا بهمدت چند ثانیه، ویژگیهای دسترسپذیری روشن میشود. با این کار نحوه عملکرد دستگاهتان تغییر میکند.\n\nویژگیهای فعلی:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nمیتوانید ویژگیهای انتخابی را در «تنظیمات > دسترسپذیری» تغییر دهید."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> روشن شود؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"با پایین نگه داشتن هردو کلید میزان صدا بهمدت چند ثانیه، <xliff:g id="SERVICE">%1$s</xliff:g> (یکی از ویژگیهای دسترسپذیری) روشن میشود. با این کار نحوه عملکرد دستگاهتان تغییر میکند.\n\nمیتوانید در «تنظیمات > دسترسپذیری»،این میانبر را به ویژگی دیگری تغییر دهید."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"روشن شود"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"روشن نشود"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0c94f35..da7ab3c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Nostetaanko äänenvoimakkuus suositellun tason yläpuolelle?\n\nPitkäkestoinen kova äänenvoimakkuus saattaa heikentää kuuloa."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Käytetäänkö esteettömyyden pikanäppäintä?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kun pikanäppäin on käytössä, voit käynnistää esteettömyystoiminnon pitämällä molempia äänenvoimakkuuspainikkeita painettuna kolmen sekunnin ajan."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Laitetaanko esteettömyysominaisuudet päälle?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Molempien äänenvoimakkuuspainikkeiden painaminen muutaman sekunnin ajan laittaa esteettömyysominaisuudet päälle. Tämä voi muuttaa laitteesi toimintaa.\n\nNykyiset ominaisuudet:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVoit muuttaa valittuja ominaisuuksia kohdassa Asetukset > Esteettömyys."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Laitetaanko <xliff:g id="SERVICE">%1$s</xliff:g> päälle?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Molempien äänenvoimakkuuspainikkeiden pitkään painaminen laittaa päälle esteettömyysominaisuuden <xliff:g id="SERVICE">%1$s</xliff:g>. Tämä voi muuttaa laitteesi toimintaa.\n\nVoit muuttaa tätä pikanäppäintä kohdassa Asetukset > Esteettömyys."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Laita päälle"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Älä laita päälle"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index dcfe8ad..d44f036 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Augmenter le volume au-dessus du niveau recommandé?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utiliser le raccourci d\'accessibilité?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour lancer une fonctionnalité d\'accessibilité."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activer les fonctionnalités d\'accessibilité?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si vous maintenez enfoncées les deux touches de volume pendant quelques secondes, vous activez les fonctionnalités d\'accessibilité. Cela peut modifier le fonctionnement de votre appareil.\n\nFonctionnalités actuellement utilisées :\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPour les modifier, sélectionnez Paramètres > Accessibilité."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activer <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si vous maintenez enfoncées les deux touches de volume pendant quelques secondes, vous activez la fonctionnalité d\'accessibilité <xliff:g id="SERVICE">%1$s</xliff:g>. Cela peut modifier le fonctionnement de votre appareil.\n\nPour attribuer ce raccourci à une autre fonctionnalité, sélectionnez Paramètres > Accessibilité."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activer"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne pas activer"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 5fc2d69..57040d0 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Augmenter le volume au dessus du niveau recommandé ?\n\nL\'écoute prolongée à un volume élevé peut endommager vos facultés auditives."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utiliser le raccourci d\'accessibilité ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quand le raccourci est activé, appuyez sur les deux boutons de volume pendant trois secondes pour démarrer une fonctionnalité d\'accessibilité."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activer les fonctionnalités d\'accessibilité ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Si vous appuyez sur les deux touches de volume pendant quelques secondes, vous activez des fonctionnalités d\'accessibilité. Cela peut affecter le fonctionnement de votre appareil.\n\nFonctionnalités actuellement utilisées :\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPour les modifier, accédez à Paramètres > Accessibilité."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activer <xliff:g id="SERVICE">%1$s</xliff:g> ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Si vous appuyez sur les deux touches de volume pendant quelques secondes, vous activez la fonctionnalité d\'accessibilité <xliff:g id="SERVICE">%1$s</xliff:g>. Cela peut affecter le fonctionnement de votre appareil.\n\nPour attribuer ce raccourci à une autre fonctionnalité, accédez à Paramètres > Accessibilité."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activer"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne pas activer"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index a6eac55..581dda5 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Queres subir o volume máis do nivel recomendado?\n\nA reprodución de son a un volume elevado durante moito tempo pode provocar danos nos oídos."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Queres utilizar o atallo de accesibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Cando o atallo está activado, podes premer os dous botóns de volume durante 3 segundos para iniciar unha función de accesibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Queres activar as funcións de accesibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ao manter as dúas teclas de volume premidas durante uns segundos actívanse as funcións de accesibilidade. Esta acción pode cambiar o funcionamento do dispositivo.\n\nFuncións activadas actualmente:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPodes cambiar as funcións seleccionadas en Configuración > Accesibilidade."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Queres activar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ao manter as dúas teclas de volume premidas durante uns segundos actívase <xliff:g id="SERVICE">%1$s</xliff:g>, unha función de accesibilidade. Esta acción pode cambiar o funcionamento do dispositivo.\n\nPodes cambiar o uso deste atallo para outra función en Configuración > Accesibilidade."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Non activar"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 6b4bbf3..c95b8eb 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ભલામણ કરેલ સ્તરની ઉપર વૉલ્યૂમ વધાર્યો?\n\nલાંબા સમય સુધી ઊંચા અવાજે સાંભળવું તમારી શ્રવણક્ષમતાને નુકસાન પહોંચાડી શકે છે."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ઍક્સેસિબિલિટી શૉર્ટકટનો ઉપયોગ કરીએ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"જ્યારે શૉર્ટકટ ચાલુ હોય, ત્યારે બન્ને વૉલ્યૂમ બટનને 3 સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા શરૂ થઈ જશે."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ઍક્સેસિબિલિટી સુવિધાઓ ચાલુ કરીએ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"બન્ને વૉલ્યૂમ કીને થોડી સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધાઓ ચાલુ થઈ જાય છે. આનાથી તમારા ડિવાઇસની કામ કરવાની રીત બદલાઈ શકે છે.\n\nવર્તમાન સુવિધાઓ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nતમે સેટિંગ > ઍક્સેસિબિલિટીમાં જઈને પસંદ કરેલી સુવિધાઓને બદલી શકો છો."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>ને ચાલુ કરીએ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"બન્ને વૉલ્યૂમ કીને થોડી સેકન્ડ સુધી દબાવી રાખવાથી ઍક્સેસિબિલિટી સુવિધા એવી <xliff:g id="SERVICE">%1$s</xliff:g> ચાલુ થઈ જાય છે. આનાથી તમારા ડિવાઇસની કામ કરવાની રીત બદલાઈ શકે છે.\n\nતમે સેટિંગ > ઍક્સેસિબિલિટીમાં જઈને આ શૉર્ટકટને બીજી સુવિધામાં બદલી શકો છો."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ચાલુ કરો"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ચાલુ કરશો નહીં"</string>
@@ -1829,8 +1831,7 @@
<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_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-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 3bf9dcd..b56fa2c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"वॉल्यूम को सुझाए गए स्तर से ऊपर बढ़ाएं?\n\nअत्यधिक वॉल्यूम पर ज़्यादा समय तक सुनने से आपकी सुनने की क्षमता को नुकसान हो सकता है."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"सुलभता शॉर्टकट का इस्तेमाल करना चाहते हैं?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"शॉर्टकट के चालू होने पर, दाेनाें वॉल्यूम बटन (आवाज़ कम या ज़्यादा करने वाले बटन) को तीन सेकंड तक दबाने से, सुलभता सुविधा शुरू हाे जाएगी."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"सुलभता सुविधाएं चालू करना चाहते हैं?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"आवाज़ कम और ज़्यादा करने वाले दोनों बटन को कुछ सेकंड तक दबाकर रखने से सुलभता सुविधाएं चालू हो जाती हैं. ऐसा करने से आपके डिवाइस के काम करने के तरीके में बदलाव हो सकता है.\n\nमौजूदा सुविधाएं:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nआप सेटिंग और सुलभता में जाकर चुनी हुई सुविधाएं बदल सकते हैं."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> चालू करना चाहते हैं?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"आवाज़ कम और ज़्यादा करने वाले दोनों बटन को कुछ सेकंड तक दबाकर रखने से <xliff:g id="SERVICE">%1$s</xliff:g> चालू हो जाती है, जो एक सुलभता सुविधा है. ऐसा करने से आपके डिवाइस के काम करने के तरीके में बदलाव हो सकता है.\n\nआप सेटिंग और सुलभता में जाकर इस शॉर्टकट को किसी दूसरी सुविधा के लिए बदल सकते हैं."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"चालू करें"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"चालू न करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index d350d04..2678cb8 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Želite li pojačati zvuk iznad preporučene razine?\n\nDugotrajno slušanje glasne glazbe može vam oštetiti sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite li upotrebljavati prečac za pristupačnost?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kad je taj prečac uključen, pritiskom na obje tipke za glasnoću na tri sekunde pokrenut će se značajka pristupačnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite li uključiti značajke pristupačnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Značajke pristupačnosti uključuju se ako na nekoliko sekundi pritisnete obje tipke za glasnoću. Time se može promijeniti način na koji vaš uređaj radi.\n\nTrenutačne značajke:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nOdabrane značajke možete promijeniti u odjeljku Postavke > Pristupačnost."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite li uključiti uslugu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ako na nekoliko sekundi pritisnete obje tipke za glasnoću, uključuje se značajka pristupačnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Time se može promijeniti način na koji vaš uređaj radi.\n\nZnačajku na koju se taj prečac odnosi možete promijeniti u odjeljku Postavke > Pristupačnost."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Uključi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nemoj uključiti"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 70f87b6..235c8fd 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Az ajánlott szint fölé szeretné emelni a hangerőt?\n\nHa hosszú időn át teszi ki magát nagy hangerőnek, azzal károsíthatja a hallását."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Szeretné használni a Kisegítő lehetőségek billentyűparancsot?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Ha a gyorsparancs aktív, akkor a két hangerőgomb három másodpercig tartó együttes lenyomásával kisegítő funkciót indíthat el."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bekapcsolja a kisegítő lehetőségeket?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"A kisegítő lehetőségek bekapcsolásához tartsa nyomva néhány másodpercig mindkét hangerőgombot. Ez hatással lehet az eszköz működésére.\n\nJelenlegi funkciók:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nA kiválasztott funkciókat a Beállítások > Kisegítő lehetőségek pontban módosíthatja."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Bekapcsolja a következőt: <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"A(z) <xliff:g id="SERVICE">%1$s</xliff:g> kisegítő lehetőség bekapcsolásához tartsa nyomva néhány másodpercig mindkét hangerőgombot. Ez hatással lehet az eszköz működésére.\n\nEzt a gyorsparancsot a Beállítások > Kisegítő lehetőségek pontban módosíthatja másik funkció használatára."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Bekapcsolom"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nem kapcsolom be"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 32ac6a8..3634908 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ձայնը բարձրացնե՞լ խորհուրդ տրվող մակարդակից ավել:\n\nԵրկարատև բարձրաձայն լսելը կարող է վնասել ձեր լսողությունը:"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Օգտագործե՞լ Մատչելիության դյուրանցումը։"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Հատուկ գործառույթն օգտագործելու համար սեղմեք և 3 վայրկյան սեղմած պահեք ձայնի ուժգնության երկու կոճակները, երբ գործառույթը միացված է:"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Միացնե՞լ հատուկ գործառույթները"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ձայնի կարգավորման երկու կոճակները մի քանի վայրկյան սեղմած պահելով կմիացնեք հատուկ գործառույթները։ Դրա արդյունքում սարքի աշխատաեղանակը կարող է փոխվել։\n\nԸնթացիկ գործառույթներ՝\n<xliff:g id="SERVICE">%1$s</xliff:g>\nԸնտրված գործառույթները փոխելու համար անցեք Կարգավորումներ > Հատուկ գործառույթներ։"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Միացնե՞լ <xliff:g id="SERVICE">%1$s</xliff:g> ծառայությունը"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ձայնի կարգավորման երկու կոճակները մի քանի վայրկյան սեղմած պահելով կմիացնեք <xliff:g id="SERVICE">%1$s</xliff:g> ծառայությունը, որը հատուկ գործառույթ է։ Դրա արդյունքում սարքի աշխատաեղանակը կարող է փոխվել։\n\nԱյս դյուրանցումը մեկ այլ գործառույթով փոխելու համար անցեք Կարգավորումներ > Հատուկ գործառույթներ։"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Միացնել"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Չմիացնել"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f8ba016..5974594 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Mengeraskan volume di atas tingkat yang disarankan?\n\nMendengarkan dengan volume keras dalam waktu yang lama dapat merusak pendengaran Anda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Aksesibilitas?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Aktifkan fitur aksesibilitas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Menahan kedua tombol volume selama beberapa detik akan mengaktifkan fitur aksesibilitas. Tindakan ini dapat mengubah cara kerja perangkat Anda.\n\nFitur saat ini:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAnda dapat mengubah fitur yang dipilih di Setelan > Aksesibilitas."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Aktifkan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Menahan kedua tombol volume selama beberapa detik akan mengaktifkan <xliff:g id="SERVICE">%1$s</xliff:g>, yang merupakan fitur aksesibilitas. Tindakan ini dapat mengubah cara kerja perangkat Anda.\n\nAnda dapat mengubah pintasan ini ke fitur lain di Setelan > Aksesibilitas."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktifkan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Jangan aktifkan"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 9278bbb..64e855f 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Hækka hljóðstyrk umfram ráðlagðan styrk?\n\nEf hlustað er á háum hljóðstyrk í langan tíma kann það að skaða heyrnina."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Viltu nota aðgengisflýtileið?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Þegar flýtileiðin er virk er kveikt á aðgengiseiginleikanum með því að halda báðum hljóðstyrkshnöppunum inni í þrjár sekúndur."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Viltu kveikja á aðgengiseiginleikum?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Kveikt er á aðgengiseiginleikum þegar báðum hljóðstyrkstökkunum er haldið inni í nokkrar sekúndur. Þetta getur breytt því hvernig tækið virkar.\n\nNúverandi eiginleikar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÞú getur breytt völdum eiginleikum í Stillingar > Aðgengi."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Viltu kveikja á <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ef báðum hljóðstyrkstökkunum er haldið inni í nokkrar sekúndur er kveikt á aðgengiseiginleikanum <xliff:g id="SERVICE">%1$s</xliff:g>. Þetta getur breytt því hvernig tækið virkar.\n\nÞú getur breytt þessari flýtileið í annan eiginleika í Stillingar > Aðgengi."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Kveikja"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ekki kveikja"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index b962d8b..185bdcf 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vuoi aumentare il volume oltre il livello consigliato?\n\nL\'ascolto ad alto volume per lunghi periodi di tempo potrebbe danneggiare l\'udito."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usare la scorciatoia Accessibilità?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando la scorciatoia è attiva, puoi premere entrambi i pulsanti del volume per tre secondi per avviare una funzione di accessibilità."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Attivare le funzioni di accessibilità?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Se tieni premuti entrambi i tasti del volume per qualche secondo, vengono attivate le funzioni di accessibilità. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nFunzioni correnti:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuoi modificare le funzioni selezionate in Impostazioni > Accessibilità."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Attivare <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Se tieni premuti entrambi i tasti del volume per qualche secondo verrà attivata la funzione di accessibilità <xliff:g id="SERVICE">%1$s</xliff:g>. Questa operazione potrebbe modificare il funzionamento del dispositivo.\n\nPuoi associare questa scorciatoia a un\'altra funzionalità in Impostazioni > Accessibilità."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Attiva"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Non attivare"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 821e4fd..963489e 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"האם להעלות את עוצמת הקול מעל לרמה המומלצת?\n\nהאזנה בעוצמת קול גבוהה למשכי זמן ממושכים עלולה לפגוע בשמיעה."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"להשתמש בקיצור הדרך לתכונת הנגישות?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"כשקיצור הדרך מופעל, לחיצה על שני לחצני עוצמת הקול למשך שלוש שניות מפעילה את תכונת הנגישות."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"להפעיל את תכונות הנגישות?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"לחיצה ארוכה על שני לחצני עוצמת הקול למשך מספר שניות מפעילה את תכונות הנגישות. בעקבות זאת, ייתכן שאופן הפעולה של המכשיר ישתנה.\n\nהתכונות הנוכחיות:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nניתן לשנות תכונות נבחרות ב\'הגדרות\' > \'נגישות\'."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"להפעיל את <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ניתן ללחוץ על שני מקשי עוצמת הקול למשך מספר שניות כדי להפעיל את <xliff:g id="SERVICE">%1$s</xliff:g>, תכונת נגישות. בעקבות זאת, ייתכן שאופן הפעולה של המכשיר ישתנה.\n\nאפשר לשנות את מקשי הקיצור האלה לתכונה נוספת ב\'הגדרות\' > \'נגישות\'."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"אני רוצה להפעיל"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"לא להפעיל"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 4b48495..ee9f0eb 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"推奨レベルを超えるまで音量を上げますか?\n\n大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ユーザー補助機能のショートカットの使用"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ショートカットが ON の場合、両方の音量ボタンを 3 秒ほど長押しするとユーザー補助機能が起動します。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ユーザー補助機能を ON にしますか?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"音量大と音量小の両方のボタンを数秒ほど長押しすると、ユーザー補助機能が ON になります。この機能が ON になると、デバイスの動作が変わることがあります。\n\n現在の機能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n選択した機能は [設定] > [ユーザー補助] で変更できます。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> を ON にしますか?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"音量大と音量小の両方のボタンを数秒ほど長押しすると、ユーザー補助機能の <xliff:g id="SERVICE">%1$s</xliff:g> が ON になります。この機能が ON になると、デバイスの動作が変わることがあります。\n\nこのショートカットは [設定] > [ユーザー補助] で別の機能に変更できます。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ON にする"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ON にしない"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index cdac647..72d9374 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"გსურთ ხმის რეკომენდებულ დონეზე მაღლა აწევა?\n\nხანგრძლივად ხმამაღლა მოსმენით შესაძლოა სმენადობა დაიზიანოთ."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"გსურთ მარტივი წვდომის მალსახმობის გამოყენება?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"თუ მალსახმობი ჩართულია, ხმის ორივე ღილაკზე 3 წამის განმავლობაში დაჭერით მარტივი წვდომის ფუნქცია ჩაირთვება."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"გსურთ, ჩართოთ მარტივი წვდომის ფუნქციები?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ხმის ორივე ღილაკზე ხანგრძლივად დაჭერა რამდენიმე წამის განმავლობაში ჩართავს მარტივი წვდომის ფუნქციებს. ამ ქმედებამ შეიძლება შეცვალოს თქვენი მოწყობილობის მუშაობის პრინციპი.\n\nამჟამინდელი ფუნქციები:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nარჩეული ფუნქციების შეცვლა შესაძლებელია აქ: პარამეტრები > მარტივი წვდომა."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ჩაირთოს <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ხმის ორივე ღილაკზე რამდენიმე წამის განმავლობაში დაჭერით ჩაირთვება <xliff:g id="SERVICE">%1$s</xliff:g>, რომელიც მარტივი წვდომის ფუნქციაა. ამან შეიძლება შეცვალოს თქვენი მოწყობილობის მუშაობის პრინციპი.\n\nამ მალსახმობის შეცვლა სხვა ფუნქციით შეგიძლიათ აქ: პარამეტრები > აპები."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ჩართვა"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"არ ჩაირთოს"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 608ed1e..ca1500d 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дыбыс деңгейін ұсынылған деңгейден көтеру керек пе?\n\nЖоғары дыбыс деңгейінде ұзақ кезеңдер бойы тыңдау есту қабілетіңізге зиян тигізуі мүмкін."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Арнайы мүмкіндік төте жолын пайдалану керек пе?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Түймелер тіркесімі қосулы кезде, екі дыбыс түймесін 3 секунд басып тұрсаңыз, \"Арнайы мүмкіндіктер\" функциясы іске қосылады."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Арнайы мүмкіндіктер іске қосылсын ба?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, арнайы мүмкіндіктер іске қосылады. Бұл – құрылғының жұмысына әсер етуі мүмкін.\n\nҚазіргі функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТаңдалған функцияларды \"Параметрлер > Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> қосылсын ба?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Дыбыс деңгейі пернелерін бірнеше секунд басып тұрсаңыз, <xliff:g id="SERVICE">%1$s</xliff:g> арнайы қызметі іске қосылады. Бұл – құрылғының жүмысына әсер етуі мүмкін.\n\nБұл таңбашаны басқа функцияға \"Параметрлер > Арнайы мүмкіндіктер\" бөлімінен өзгерте аласыз."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Қосылсын"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Қосылмасын"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 14b0189..4424426 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"បង្កើនកម្រិតសំឡេងលើសពីកម្រិតបានផ្ដល់យោបល់?\n\nការស្ដាប់នៅកម្រិតសំឡេងខ្លាំងយូរអាចធ្វើឲ្យខូចត្រចៀក។"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ប្រើប្រាស់ផ្លូវកាត់ភាពងាយស្រួល?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"នៅពេលបើកផ្លូវកាត់ ការចុចប៊ូតុងកម្រិតសំឡេងទាំងពីររយៈពេល 3 វិនាទីនឹងចាប់ផ្តើមមុខងារភាពងាយប្រើ។"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"បើកមុខងារភាពងាយប្រើឬ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ការសង្កត់គ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់រយៈពេលពីរបីវិនាទីនឹងបើកមុខងារភាពងាយប្រើ។ ការធ្វើបែបនេះអាចផ្លាស់ប្ដូររបៀបដែលឧបករណ៍របស់អ្នកដំណើរការ។\n\nមុខងារបច្ចុប្បន្ន៖\n<xliff:g id="SERVICE">%1$s</xliff:g>\nអ្នកអាចប្ដូរមុខងារដែលបានជ្រើសរើសនៅក្នុងការកំណត់ > ភាពងាយស្រួល។"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"បើក <xliff:g id="SERVICE">%1$s</xliff:g> ឬ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ការសង្កត់គ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់រយៈពេលពីរបីវិនាទីនឹងបើក <xliff:g id="SERVICE">%1$s</xliff:g> ដែលជាមុខងារភាពងាយប្រើ។ ការធ្វើបែបនេះអាចផ្លាស់ប្ដូររបៀបដែលឧបករណ៍របស់អ្នកដំណើរការ។\n\nអ្នកអាចប្ដូរផ្លូវកាត់នេះទៅមុខងារផ្សេងទៀតនៅក្នុងការកំណត់ > ភាពងាយស្រួល។"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"បើក"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"កុំបើក"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 0fe1c24..4e2e7e0 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ವಾಲ್ಯೂಮ್ ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾದ ಮಟ್ಟಕ್ಕಿಂತಲೂ ಹೆಚ್ಚು ಮಾಡುವುದೇ?\n\nದೀರ್ಘ ಅವಧಿಯವರೆಗೆ ಹೆಚ್ಚಿನ ವಾಲ್ಯೂಮ್ನಲ್ಲಿ ಆಲಿಸುವುದರಿಂದ ನಿಮ್ಮ ಆಲಿಸುವಿಕೆ ಸಾಮರ್ಥ್ಯಕ್ಕೆ ಹಾನಿಯುಂಟು ಮಾಡಬಹುದು."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ಪ್ರವೇಶಿಸುವಿಕೆ ಶಾರ್ಟ್ಕಟ್ ಬಳಸುವುದೇ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ಶಾರ್ಟ್ಕಟ್ ಆನ್ ಆಗಿರುವಾಗ, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಬಟನ್ಗಳನ್ನು 3 ಸೆಕೆಂಡುಗಳ ಕಾಲ ಒತ್ತಿದರೆ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವೊಂದು ಪ್ರಾರಂಭವಾಗುತ್ತದೆ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆನ್ ಮಾಡಬೇಕೇ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯಗಳು ಆನ್ ಆಗುತ್ತವೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\n ಪ್ರಸ್ತುತ ವೈಶಿಷ್ಟ್ಯಗಳು:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಆ್ಯಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿ ಆಯ್ದ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ನೀವು ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡುವುದೇ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಕೆಲವು ಸೆಕೆಂಡುಗಳ ಕಾಲ ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳುವುದರಿಂದ ಪ್ರವೇಶಿಸುವಿಕೆ ವೈಶಿಷ್ಟ್ಯವಾದ <xliff:g id="SERVICE">%1$s</xliff:g> ಆನ್ ಆಗುತ್ತದೆ. ಇದು ನಿಮ್ಮ ಸಾಧನವು ಹೇಗೆ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಬದಲಾಯಿಸಬಹುದು.\n\nನೀವು ಈ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಅಕ್ಸೆಸಿಬಿಲಿಟಿಯಲ್ಲಿನ ಮತ್ತೊಂದು ವೈಶಿಷ್ಟ್ಯಕ್ಕೆ ಬದಲಾಯಿಸಬಹುದು."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ಆನ್ ಮಾಡಿ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ಆನ್ ಮಾಡಬೇಡಿ"</string>
@@ -1829,8 +1831,7 @@
<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_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-ko/strings.xml b/core/res/res/values-ko/strings.xml
index e3406c1b..43c785a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"권장 수준 이상으로 볼륨을 높이시겠습니까?\n\n높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"접근성 단축키를 사용하시겠습니까?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"단축키가 사용 설정된 경우 볼륨 버튼 두 개를 동시에 3초간 누르면 접근성 기능이 시작됩니다."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"접근성 기능을 사용하시겠습니까?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"볼륨 키 2개를 몇 초 동안 길게 누르면 접근성 기능이 사용 설정됩니다. 이때 기기 작동 방식이 달라질 수 있습니다.\n\n현재 기능:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n설정 > 접근성에서 선택한 기능을 변경할 수 있습니다."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>을(를) 사용하시겠습니까?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"볼륨 키 2개를 몇 초 동안 길게 누르면 <xliff:g id="SERVICE">%1$s</xliff:g> 접근성 기능이 사용 설정됩니다. 이렇게 되면 기기 작동 방식이 달라질 수 있습니다.\n\n설정 > 접근성에서 이 단축키를 다른 기능으로 변경할 수 있습니다."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"사용"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"사용 안 함"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 3268d16..182af1b 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Сунушталган деңгээлден да катуулатып уккуңуз келеби?\n\nМузыканы узакка чейин катуу уксаңыз, угууңуз начарлап кетиши мүмкүн."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ыкчам иштетесизби?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Атайын мүмкүнчүлүктөр функциясын пайдалануу үчүн ал күйгүзүлгөндө, үндү катуулатып/акырындаткан эки баскычты тең 3 секунддай коё бербей басып туруңуз."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Атайын мүмкүнчүлүктөрдү иштетесизби?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Атайын мүмкүнчүлүктөр функциясын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nУчурдагы функциялар:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТандалган функцияларды өзгөртүү үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> күйгүзүлсүнбү?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> кызматын иштетүү үчүн, үндү чоңойтуп/кичирейтүү баскычтарын бир нече секунд коё бербей басып туруңуз. Ушуну менен, түзмөгүңүз бир аз башкача иштеп калышы мүмкүн.\n\nБаскычтардын ушул айкалышын башка функцияга дайындоо үчүн, Жөндөөлөр > Атайын мүмкүнчүлүктөр бөлүмүнө өтүңүз."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ооба"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Жок"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index d22ce0c..9f964eb 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ເພີ່ມລະດັບສຽງໃຫ້ເກີນກວ່າລະດັບທີ່ແນະນຳບໍ?\n\nການຮັບຟັງສຽງໃນລະດັບທີ່ສູງເປັນໄລຍະເວລາດົນອາດເຮັດໃຫ້ການຟັງຂອງທ່ານມີບັນຫາໄດ້."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ໃຊ້ປຸ່ມລັດການຊ່ວຍເຂົ້າເຖິງບໍ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ເມື່ອເປີດໃຊ້ທາງລັດແລ້ວ, ການກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ 3 ວິນາທີຈະເປັນການເລີ່ມຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ເປີດໃຊ້ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງບໍ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ສອງສາມວິນາທີເພື່ອເປີດໃຊ້ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ນີ້ອາດປ່ຽນວິທີການເຮັດວຽກຂອງອຸປະກອນທ່ານ.\n\nຄຸນສົມບັດປັດຈຸບັນ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nທ່ານສາມາດປ່ຽນຄຸນສົມບັດທີ່ເລືອກໄດ້ໃນການຕັ້ງຄ່າ > ການຊ່ວຍເຂົ້າເຖິງ."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ເປີດໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g> ບໍ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ສອງສາມວິນາທີເພື່ອເປີດໃຊ້ <xliff:g id="SERVICE">%1$s</xliff:g>, ຄຸນສົມບັດການຊ່ວຍເຂົ້າເຖິງ. ນີ້ອາດປ່ຽນວິທີການເຮັດວຽກຂອງອຸປະກອນທ່ານ.\n\nທ່ານສາມາດປ່ຽນທາງລັດນີ້ເປັນຄຸນສົມບັດອື່ນໄດ້ໃນການຕັ້ງຄ່າ > ການຊ່ວຍເຂົ້າເຖິງ."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ເປີດໃຊ້"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ບໍ່ເປີດ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ff687c5..337b31c 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Padidinti garsą daugiau nei rekomenduojamas lygis?\n\nIlgai klausydami dideliu garsu galite pažeisti klausą."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Naudoti spartųjį pritaikymo neįgaliesiems klavišą?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kai spartusis klavišas įjungtas, paspaudus abu garsumo mygtukus ir palaikius 3 sekundes bus įjungta pritaikymo neįgaliesiems funkcija."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Įjungti pritaikymo neįgaliesiems funkcijas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Paspaudus abu garsumo klavišus ir palaikius kelias sekundes įjungiamos pritaikymo neįgaliesiems funkcijos. Tai gali pakeisti įrenginio veikimą.\n\nDabartinės funkcijos:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPasirinktas funkcijas galite pakeisti skiltyje „Nustatymai“ > „Pritaikomumas“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Įjungti „<xliff:g id="SERVICE">%1$s</xliff:g>“?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Paspaudus abu garsumo klavišus ir palaikius kelias sekundes įjungiama pritaikymo neįgaliesiems funkcija „<xliff:g id="SERVICE">%1$s</xliff:g>“. Tai gali pakeisti įrenginio veikimą.\n\nGalite pakeisti šį spartųjį klavišą į kitą funkciją skiltyje „Nustatymai“ > „Pritaikomumas“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Įjungti"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Neįjungti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2cb6058..2b92b44 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vai palielināt skaļumu virs ieteicamā līmeņa?\n\nIlgstoši klausoties skaņu lielā skaļumā, var tikt bojāta dzirde."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vai izmantot pieejamības saīsni?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kad īsinājumtaustiņš ir ieslēgts, nospiežot abas skaļuma pogas un 3 sekundes turot tās, tiks aktivizēta pieejamības funkcija."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vai ieslēgt pieejamības funkcijas?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Turot nospiestus abus skaļuma taustiņus dažas sekundes, tiek ieslēgtas pieejamības funkcijas. Tas var mainīt ierīces darbību.\n\nPašreizējās funkcijas:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAtlasītās funkcijas varat mainīt šeit: Iestatījumi > Pieejamība."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vai ieslēgt pakalpojumu <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Turot nospiestus abus skaļuma taustiņus dažas sekundes, tiek ieslēgta pieejamības funkcija <xliff:g id="SERVICE">%1$s</xliff:g>. Tas var mainīt ierīces darbību.\n\nŠo saīsni uz citu funkciju varat mainīt šeit: Iestatījumi > Pieejamība."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ieslēgt"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Neieslēgt"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index bf7220c..ccd1ef7 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Да го зголемиме звукот над препорачаното ниво?\n\nСлушањето звуци со голема јачина подолги периоди може да ви го оштети сетилото за слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Да се користи кратенка за „Пристапност“?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Кога е вклучена кратенката, ако ги притиснете двете копчиња за јачина на звук во времетраење од 3 секунди, ќе се стартува функција за пристапност."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Да се вклучат функциите за пристапност?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ако ги задржите притиснати двете копчиња за јачина на звук неколку секунди, ќе се вклучат функциите за пристапност. Ова може да го промени начинот на функционирање на уредот.\n\nТековни функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМоже да ги промените избраните функции во „Поставки > Пристапност“."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Да се вклучи <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ако ги задржите притиснати двете копчиња за јачина на звук неколку секунди, ќе се вклучи функцијата за пристапност <xliff:g id="SERVICE">%1$s</xliff:g>. Ова може да го промени начинот на функционирање на уредот.\n\nМоже да ја измените кратенкава да биде за друга функција во „Поставки > Пристапност“."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Вклучи"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не вклучувај"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 513ed1d..8ec1c68 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"മുകളിൽക്കൊടുത്തിരിക്കുന്ന ശുപാർശചെയ്ത ലെവലിലേക്ക് വോളിയം വർദ്ധിപ്പിക്കണോ?\n\nഉയർന്ന വോളിയത്തിൽ ദീർഘനേരം കേൾക്കുന്നത് നിങ്ങളുടെ ശ്രവണ ശേഷിയെ ദോഷകരമായി ബാധിക്കാം."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ഉപയോഗസഹായി കുറുക്കുവഴി ഉപയോഗിക്കണോ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"കുറുക്കുവഴി ഓണായിരിക്കുമ്പോൾ, രണ്ട് വോളിയം ബട്ടണുകളും 3 സെക്കൻഡ് നേരത്തേക്ക് അമർത്തുന്നത് ഉപയോഗസഹായി ഫീച്ചർ ആരംഭിക്കും."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കണോ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത്, ഉപയോഗസഹായി ഫീച്ചറുകൾ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന രീതിയെ ഇത് മാറ്റിയേക്കാം.\n\nനിലവിലുള്ള ഫീച്ചറുകൾ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nതിരഞ്ഞെടുത്ത ഫീച്ചറുകൾ ക്രമീകരണം > ഉപയോഗസഹായി എന്നതിൽ മാറ്റാനാവും."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ഓണാക്കണോ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"രണ്ട് വോളിയം കീകളും അൽപ്പ നേരത്തേക്ക് അമർത്തിപ്പിടിക്കുന്നത് ഉപയോഗസഹായി ഫീച്ചറായ <xliff:g id="SERVICE">%1$s</xliff:g> എന്നതിനെ ഓണാക്കുന്നു. നിങ്ങളുടെ ഉപകരണം പ്രവർത്തിക്കുന്ന വിധം ഇത് മാറ്റിയേക്കാം.\n\nക്രമീകരണം > ഉപയോഗസഹായി എന്നതിലെ മറ്റൊരു ഫീച്ചറിലേക്ക് നിങ്ങൾക്ക് ഈ കുറുക്കുവഴി മാറ്റാനാവും."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ഓണാക്കുക"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ഓണാക്കരുത്"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index b95b689..6b7a365 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Дууг санал болгосноос чанга болгож өсгөх үү?\n\nУрт хугацаанд чанга хөгжим сонсох нь таны сонсголыг муутгаж болно."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Хүртээмжийн товчлолыг ашиглах уу?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Товчлол асаалттай үед дууны түвшний хоёр товчлуурыг хамтад нь 3 секунд дарснаар хандалтын онцлогийг эхлүүлнэ."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Хандалтын онцлогуудыг асаах уу?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарснаар хандалтын онцлогууд асна. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nОдоогийн онцлогууд:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nТа сонгосон онцлогуудыг Тохиргоо > Хандалт хэсэгт өөрчлөх боломжтой."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g>-г асаах уу?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Дууны түвшний түлхүүрийг хэдэн секундийн турш зэрэг дарах нь хандалтын онцлог болох <xliff:g id="SERVICE">%1$s</xliff:g>-г асаадаг. Энэ нь таны төхөөрөмжийн ажиллах зарчмыг өөрчилж болзошгүй.\n\nТа Тохиргоо > Хандалт хэсэгт энэ товчлолыг өөр онцлогт оноож өөрчлөх боломжтой."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Асаах"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Бүү асаа"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 40c7dbd..4f8925c 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"शिफारस केलेल्या पातळीच्या वर आवाज वाढवायचा?\n\nउच्च आवाजात दीर्घ काळ ऐकण्याने आपल्या श्रवणशक्तीची हानी होऊ शकते."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"प्रवेशयोग्यता शॉर्टकट वापरायचा?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"शॉर्टकट सुरू असताना, दोन्ही व्हॉल्यूम बटणे तीन सेकंदांसाठी दाबून ठेवल्याने अॅक्सेसिबिलिटी वैशिष्ट्य सुरू होईल."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"अॅक्सेसिबिलिटी वैशिष्ट्ये सुरू करायची आहेत का?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"दोन्ही व्हॉल्यूम की काही सेकंद धरून ठेवल्याने अॅक्सेसिबिलिटी वैशिष्ट्ये सुरू होतात. यामुळे तुमचे डिव्हाइस कसे काम करते हे पूर्णपणे बदलते.\n\nसध्याची वैशिष्ट्ये:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nतुम्ही हा शॉर्टकट सेटिंग्ज > अॅक्सेसिबिलिटी मध्ये बदलू शकता."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> सुरू करायची आहे का?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"दोन्ही व्हॉल्यूम की काही सेकंद धरून ठेवल्याने <xliff:g id="SERVICE">%1$s</xliff:g>, एक अॅक्सेसिबिलिटी वैशिष्ट्य सुरू होते. यामुळे तुमचे डिव्हाइस कसे काम करते हे पूर्णपणे बदलते.\n\nतुम्ही हा शॉर्टकट सेटिंग्ज > अॅक्सेसिबिलिटी मध्ये बदलू शकता."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"सुरू करा"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"सुरू करू नका"</string>
@@ -1829,8 +1831,7 @@
<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_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-ms/strings.xml b/core/res/res/values-ms/strings.xml
index fb24a06..161be37 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Naikkan kelantangan melebihi paras yang disyokorkan?\n\nMendengar pada kelantangan yang tinggi untuk tempoh yang lama boleh merosakkan pendengaran anda."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gunakan Pintasan Kebolehaksesan?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Apabila pintasan dihidupkan, tindakan menekan kedua-dua butang kelantangan selama 3 saat akan memulakan ciri kebolehaksesan."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Hidupkan ciri kebolehaksesan?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Tindakan menahan kedua-dua kekunci kelantangan selama beberapa saat akan menghidupkan ciri kebolehaksesan. Hal ini mungkin mengubah cara peranti anda berfungsi.\n\nCiri semasa:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nAnda boleh menukar ciri yang dipilih dalam Tetapan > Kebolehaksesan."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Hidupkan <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Tindakan menahan kedua-dua kekunci kelantangan selama beberapa saat akan menghidupkan <xliff:g id="SERVICE">%1$s</xliff:g>, iaitu satu ciri kebolehaksesan. Ini mungkin mengubah cara peranti anda berfungsi.\n\nAnda boleh menukar pintasan ini kepada ciri lain dalam Tetapan > Kebolehaksesan."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Hidupkan"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Jangan hidupkan"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 2c3fbcb..a21e4bc 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"အသံကို အကြံပြုထားသည့် ပမာဏထက် မြှင့်ပေးရမလား?\n\nအသံကို မြင့်သည့် အဆင့်မှာ ကြာရှည်စွာ နားထောင်ခြင်းက သင်၏ နားကို ထိခိုက်စေနိုင်သည်။"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"အများသုံးစွဲနိုင်မှု ဖြတ်လမ်းလင့်ခ်ကို အသုံးပြုလိုပါသလား။"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ဖြတ်လမ်းလင့်ခ်ကို ဖွင့်ထားစဉ် အသံထိန်းခလုတ် နှစ်ခုစလုံးကို ၃ စက္ကန့်ခန့် ဖိထားခြင်းဖြင့် အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုကို ဖွင့်နိုင်သည်။"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်မလား။"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုများ ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nလက်ရှိ ဝန်ဆောင်မှုများ-\n<xliff:g id="SERVICE">%1$s</xliff:g>\n\'ဆက်တင်များ > အများသုံးစွဲနိုင်မှု\' တွင် ရွေးထားသည့် ဝန်ဆောင်မှုများကို ပြောင်းနိုင်သည်။"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ဖွင့်မလား။"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"အသံခလုတ်နှစ်ခုလုံးကို စက္ကန့်အနည်းငယ် ဖိထားခြင်းက အများသုံးစွဲနိုင်မှုဆိုင်ရာ ဝန်ဆောင်မှုဖြစ်သော <xliff:g id="SERVICE">%1$s</xliff:g> ကို ဖွင့်ပေးသည်။ ဤလုပ်ဆောင်ချက်က သင့်စက်အလုပ်လုပ်ပုံကို ပြောင်းလဲနိုင်သည်။\n\nဤဖြတ်လမ်းလင့်ခ်ကို \'ဆက်တင်များ > အများသုံးစွဲနိုင်မှု\' တွင် နောက်ဝန်ဆောင်မှုတစ်ခုသို့ ပြောင်းနိုင်သည်။"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ဖွင့်ရန်"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"မဖွင့်ပါနှင့်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index fced207..d25c3ca 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vil du øke volumet til over anbefalt nivå?\n\nHvis du hører på et høyt volum over lengre perioder, kan det skade hørselen din."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vil du bruke tilgjengelighetssnarveien?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Når snarveien er på, starter en tilgjengelighetsfunksjon når du trykker inn begge volumknappene i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vil du slå på tilgjengelighetsfunksjoner?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hvis du holder inne volumtastene i noen sekunder, slås tilgjengelighetsfunksjoner på. Dette kan endre hvordan enheten din fungerer.\n\nNåværende funksjoner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan endre valgte funksjoner i Innstillinger > Tilgjengelighet."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vil du slå på <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hvis du holder inne begge volumtastene i noen sekunder, slår du på <xliff:g id="SERVICE">%1$s</xliff:g>, en tilgjengelighetsfunksjon. Dette kan endre hvordan enheten din fungerer.\n\nDu kan endre denne snarveien til en annen funksjon i Innstillinger > Tilgjengelighet."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Slå på"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ikke slå på"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index fadabe8..2fc711f 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"सिफारिस तहभन्दा आवाज ठुलो गर्नुहुन्छ?\n\nलामो समय सम्म उच्च आवाजमा सुन्दा तपाईँको सुन्ने शक्तिलाई हानी गर्न सक्छ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"पहुँच सम्बन्धी सर्टकट प्रयोग गर्ने हो?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"यो सर्टकट सक्रिय हुँदा, ३ सेकेन्डसम्म दुवै भोल्युम बटन थिच्नुले पहुँचसम्बन्धी कुनै सुविधा सुरु गर्ने छ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"पहुँचसम्बन्धी सुविधाहरू सक्रिय गर्ने हो?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुभयो भने पहुँचसम्बन्धी सुविधाहरू सक्रिय हुन्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nहालका सुविधाहरू:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nतपाईं सेटिङ > पहुँचमा गएर चयन गरिएका सुविधाहरू परिवर्तन गर्न सक्नुहुन्छ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> सक्रिय गर्ने हो?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"केही सेकेन्डसम्म दुवै भोल्युम बटन थिचिराख्नुले <xliff:g id="SERVICE">%1$s</xliff:g> नामक पहुँचसम्बन्धी सुविधा सक्रिय गर्छ। यसले तपाईंको यन्त्रले काम गर्ने तरिका परिवर्तन गर्न सक्छ।\n\nतपाईं सेटिङ > पहुँचमा गई यो सर्टकटमार्फत अर्को सुविधा खुल्ने बनाउन सक्नुहुन्छ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"सक्रिय गरियोस्"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"सक्रिय नगरियोस्"</string>
@@ -1829,8 +1831,7 @@
<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_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-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 337bf17..11a873a 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Volume verhogen tot boven het aanbevolen niveau?\n\nAls je langere tijd op hoog volume naar muziek luistert, raakt je gehoor mogelijk beschadigd."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Snelkoppeling toegankelijkheid gebruiken?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Als de snelkoppeling is ingeschakeld, kun je drie seconden op beide volumeknoppen drukken om een toegankelijkheidsfunctie te starten."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Toegankelijkheidsfuncties inschakelen?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Als je beide volumetoetsen een paar seconden ingedrukt houdt, schakel je de toegankelijkheidsfuncties in. Hierdoor kan de manier veranderen waarop je apparaat werkt.\n\nHuidige functies:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nJe kunt de geselecteerde functies wijzigen via Instellingen > Toegankelijkheid."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> inschakelen?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Als je beide volumetoetsen een paar seconden ingedrukt houdt, wordt de toegankelijkheidsfunctie <xliff:g id="SERVICE">%1$s</xliff:g> ingeschakeld. Hierdoor kan de manier veranderen waarop je apparaat werkt.\n\nJe kunt deze sneltoets op een andere functie instellen via Instellingen > Toegankelijkheid."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Inschakelen"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Niet inschakelen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 85ca065..188a9bc 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ମାତ୍ରା ବଢ଼ାଇ ସୁପାରିଶ ସ୍ତର ବଢ଼ାଉଛନ୍ତି? \n\n ଲମ୍ବା ସମୟ ପର୍ଯ୍ୟନ୍ତ ଉଚ୍ଚ ଶବ୍ଦରେ ଶୁଣିଲେ ଆପଣଙ୍କ ଶ୍ରବଣ ଶକ୍ତି ଖରାପ ହୋଇପାରେ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ଆକ୍ସେସବିଲିଟି ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରିବେ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ସର୍ଟକଟ୍ ଚାଲୁ ଥିବା ବେଳେ, ଉଭୟ ଭଲ୍ୟୁମ୍ ବଟନ୍ 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇବା ଦ୍ୱାରା ଏକ ଆକ୍ସେସବିଲିଟି ଫିଚର୍ ଆରମ୍ଭ ହେବ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ଆକ୍ସେସିବିଲିଟୀ ଫିଚରଗୁଡ଼ିକୁ ଚାଲୁ କରିବେ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"କିଛି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ’କୁ ଧରି ରଖିବା ଫଳରେ ଆକ୍ସେସିବିଲିଟୀ ଫିଚରଗୁଡ଼ିକ ଚାଲୁ ହୁଏ। ଏହା ଆପଣଙ୍କ ଡିଭାଇସ୍ କିପରି କାମ କରେ ତାହା ପରିବର୍ତ୍ତନ କରିପାରେ।\n\nବର୍ତ୍ତମାନର ଫିଚରଗୁଡ଼ିକ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n ଆପଣ ସେଟିଂସ୍ &gt ଆକ୍ସେସିବିଲିଟୀରେ ଚୟନିତ ଫିଚରଗୁଡ଼ିକୁ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ଚାଲୁ କରିବେ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"କିଛି ସେକେଣ୍ଡ ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ୍ କୀ’କୁ ଧରି ରଖିବା ଫଳରେ ଏକ ଆକ୍ସେସିବିଲିଟୀ ଫିଚର୍ <xliff:g id="SERVICE">%1$s</xliff:g> ଚାଲୁ ହୁଏ। ଏହା ଆପଣଙ୍କ ଡିଭାଇସ୍ କିପରି କାମ କରେ ତାହା ପରିବର୍ତ୍ତନ କରିପାରେ।\n\nଆପଣ ସେଟିଂସ୍ &gt ଆକ୍ସେସିବିଲିଟୀରେ ଏହି ସର୍ଚକଟକୁ ଅନ୍ୟ ଏକ ଫିଚରରେ ପରିବର୍ତ୍ତନ କରିପାରିବେ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ଚାଲୁ କରନ୍ତୁ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ଚାଲୁ କରନ୍ତୁ ନାହିଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index ed663a8..22d5f441 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"ਕੀ ਵੌਲਿਊਮ ਸਿਫ਼ਾਰਸ਼ ਕੀਤੇ ਪੱਧਰ ਤੋਂ ਵਧਾਉਣੀ ਹੈ?\n\nਲੰਮੇ ਸਮੇਂ ਤੱਕ ਉੱਚ ਵੌਲਿਊਮ ਤੇ ਸੁਣਨ ਨਾਲ ਤੁਹਾਡੀ ਸੁਣਨ ਸ਼ਕਤੀ ਨੂੰ ਨੁਕਸਾਨ ਪਹੁੰਚ ਸਕਦਾ ਹੈ।"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਸ਼ਾਰਟਕੱਟ ਵਰਤਣਾ ਹੈ?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ਸ਼ਾਰਟਕੱਟ ਚਾਲੂ ਹੋਣ \'ਤੇ, ਕਿਸੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ ਬਟਨਾਂ ਨੂੰ 3 ਸਕਿੰਟ ਲਈ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ਕੀ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"ਕੁਝ ਸਕਿੰਟਾਂ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਨੂੰ ਦਬਾਈ ਰੱਖਣਾ, ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਚਾਲੂ ਕਰ ਦਿੰਦਾ ਹੈ। ਇਹ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲ ਸਕਦਾ ਹੈ।\n\nਮੌਜੂਦਾ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nਸੈਟਿੰਗਾਂ ਅਤੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਤੁਸੀਂ ਚੁਣੀਆਂ ਗਈਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕਰਨਾ ਹੈ?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"ਕੁਝ ਸਕਿੰਟਾਂ ਲਈ ਦੋਵੇਂ ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਨੂੰ ਦਬਾਈ ਰੱਖਣਾ <xliff:g id="SERVICE">%1$s</xliff:g>, ਇੱਕ ਪਹੁੰਚਯੋਗਤਾ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਚਾਲੂ ਕਰ ਦਿੰਦਾ ਹੈ। ਇਹ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੇ ਕੰਮ ਕਰਨ ਦੇ ਤਰੀਕੇ ਨੂੰ ਬਦਲ ਸਕਦਾ ਹੈ।\n\nਸੈਟਿੰਗਾਂ ਅਤੇ ਪਹੁੰਚਯੋਗਤਾ ਵਿੱਚ ਤੁਸੀਂ ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਕਿਸੇ ਹੋਰ ਵਿਸ਼ੇਸ਼ਤਾ ਵਿੱਚ ਬਦਲ ਸਕਦੇ ਹੋ।"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ਚਾਲੂ ਕਰੋ"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ਚਾਲੂ ਨਾ ਕਰੋ"</string>
@@ -1829,8 +1831,7 @@
<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_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-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 16b3faf..c9c3b46 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zwiększyć głośność ponad zalecany poziom?\n\nSłuchanie głośno przez długi czas może uszkodzić Twój słuch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Użyć skrótu do ułatwień dostępu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Gdy skrót jest włączony, jednoczesne naciskanie przez trzy sekundy obu przycisków głośności uruchamia funkcję ułatwień dostępu."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Włączyć ułatwienia dostępu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Przytrzymanie obu klawiszy głośności przez kilka sekund włącza ułatwienia dostępu. Może to zmienić sposób działania urządzenia.\n\nBieżące funkcje:\n<xliff:g id="SERVICE">%1$s</xliff:g>\naby zmienić wybrane funkcje, kliknij Ustawienia > Ułatwienia dostępu."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Włączyć usługę <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Przytrzymanie obu klawiszy głośności przez kilka sekund włącza usługę <xliff:g id="SERVICE">%1$s</xliff:g>, stanowiącą ułatwienie dostępu. Może to zmienić sposób działania urządzenia.\n\nAby zmienić ten skrót i wskazać inną funkcję, kliknij Ustawienia > Ułatwienia dostępu."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Włącz"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nie włączaj"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 2883d6f..221e29c 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ativar os recursos de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter as duas teclas de volume pressionadas por alguns segundos ativa os recursos de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nRecursos atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÉ possível mudar os recursos selecionados em \"Config. > Acessibilidade\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ativar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter as duas teclas de volume pressionadas por alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, um recurso de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nÉ possível trocar o uso desse atalho para outro recurso em \"Config. > Acessibilidade\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3bc9530..e908c7f 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir com um volume elevado durante longos períodos poderá ser prejudicial para a sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Pretende utilizar o atalho de acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho está ativado, premir ambos os botões de volume durante 3 segundos inicia uma funcionalidade de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Pretende ativar as funcionalidades de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter premidas ambas as teclas de volume durante alguns segundos ativa as funcionalidades de acessibilidade. Estas podem alterar a forma como o seu dispositivo funciona.\n\nFuncionalidades atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\npode alterar as funcionalidades selecionadas em Definições > Acessibilidade."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Pretende ativar o serviço <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter premidas ambas as teclas de volume durante alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, uma funcionalidade de acessibilidade. Esta pode alterar a forma como o seu dispositivo funciona.\n\nPode alterar este atalho para outra funcionalidade em Definições > Acessibilidade."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 2883d6f..221e29c 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Aumentar o volume acima do nível recomendado?\n\nOuvir em volume alto por longos períodos pode danificar sua audição."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Usar atalho de Acessibilidade?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Quando o atalho estiver ativado, pressione os dois botões de volume por três segundos para iniciar um recurso de acessibilidade."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ativar os recursos de acessibilidade?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Manter as duas teclas de volume pressionadas por alguns segundos ativa os recursos de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nRecursos atuais:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nÉ possível mudar os recursos selecionados em \"Config. > Acessibilidade\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ativar <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Manter as duas teclas de volume pressionadas por alguns segundos ativa o serviço <xliff:g id="SERVICE">%1$s</xliff:g>, um recurso de acessibilidade. Isso pode mudar a forma como seu dispositivo funciona.\n\nÉ possível trocar o uso desse atalho para outro recurso em \"Config. > Acessibilidade\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Ativar"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Não ativar"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index d5a9297..c2dea7e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utilizați comanda rapidă pentru accesibilitate?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Atunci când comanda rapidă este activată, dacă apăsați ambele butoane de volum timp de trei secunde, veți lansa o funcție de accesibilitate."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Activați funcțiile de accesibilitate?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Dacă apăsați ambele taste de volum câteva secunde, activați funcțiile de accesibilitate. Acest lucru poate schimba funcționarea dispozitivului.\n\nFuncțiile actuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuteți schimba funcțiile selectate din Setări > Accesibilitate."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Activați <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Dacă apăsați ambele taste de volum câteva secunde, activați funcția de accesibilitate <xliff:g id="SERVICE">%1$s</xliff:g>. Acest lucru poate schimba funcționarea dispozitivului.\n\nPuteți alege altă funcție pentru această comandă în Setări > Accesibilitate."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activați"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nu activați"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 3339fb5..f8d6dfb 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Установить громкость выше рекомендуемого уровня?\n\nВоздействие громкого звука в течение долгого времени может привести к повреждению слуха."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Использовать быстрое включение?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Чтобы использовать функцию специальных возможностей, когда она включена, нажмите и удерживайте обе кнопки регулировки громкости в течение трех секунд."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Включить специальные возможности?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Чтобы включить специальные возможности, нажмите обе кнопки регулировки громкости и удерживайте несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nТекущие функции:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nЧтобы изменить выбранные функции, перейдите в настройки и нажмите \"Специальные возможности\"."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Включить функцию \"<xliff:g id="SERVICE">%1$s</xliff:g>\"?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Чтобы включить функцию \"<xliff:g id="SERVICE">%1$s</xliff:g>\", нажмите обе кнопки регулировки громкости на несколько секунд. Обратите внимание, что в работе устройства могут произойти изменения.\n\nЧтобы назначить это сочетание клавиш другой функции, перейдите в настройки и выберите \"Специальные возможности\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Включить"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не включать"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c26f23f..63615f7 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"නිර්දේශිතයි මට්ටමට වඩා ශබ්දය වැඩිද?\n\nදිගු කාලයක් සඳහා ඉහළ ශබ්දයක් ඇසීමෙන් ඇතැම් විට ඔබගේ ඇසීමට හානි විය හැක."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ප්රවේශ්යතා කෙටිමඟ භාවිතා කරන්නද?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"කෙටිමග ක්රියාත්මක විට, හඬ පරිමා බොත්තම් දෙකම තත්පර 3ක් තිස්සේ එබීමෙන් ප්රවේශ්යතා විශේෂාංගය ආරම්භ වනු ඇත."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ප්රවේශ්යතා විශේෂාංග ක්රියාත්මක කරන්නද?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"හඬ පරිමා යතුරු දෙකම තත්පර කීපයකට පහළට අල්ලාගෙන සිටීම ප්රවේශ්යතා විශේෂාංග ක්රියාත්මක කරයි. මෙය ඔබේ උපාංගය ක්රියා කරන ආකාරය වෙනස් කළ හැකිය.\n\nවත්මන් විශේෂාංග:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nඔබට තේරූ විශේෂාංග සැකසීම් > ප්රවේශ්යතාව හි වෙනස් කළ හැකිය."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ක්රියාත්මක කරන්නද?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"හඬ පරිමා යතුරු දෙකම තත්පර කීපයකට පහළට අල්ලාගෙන සිටීම ප්රවේශ්යතා විශේෂාංගයක් වන <xliff:g id="SERVICE">%1$s</xliff:g> ක්රියාත්මක කරයි. මෙය ඔබේ උපාංගය ක්රියා කරන ආකාරය වෙනස් කළ හැකිය.\n\nඔබට මෙම කෙටිමග සැකසීම් > ප්රවේශ්යතාව හි තවත් විශේෂාංගයකට වෙනස් කළ හැකිය."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ක්රියාත්මක කරන්න"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ක්රියාත්මක නොකරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 1a4f07e..4f0704b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Zvýšiť hlasitosť nad odporúčanú úroveň?\n\nDlhodobé počúvanie pri vysokej hlasitosti môže poškodiť váš sluch."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Použiť skratku dostupnosti?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Keď je skratka zapnutá, stlačením obidvoch tlačidiel hlasitosti na tri sekundy spustíte funkciu dostupnosti."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Chcete zapnúť funkcie dostupnosti?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Pridržaním oboch tlačidiel hlasitosti na niekoľko sekúnd zapnete funkcie dostupnosti. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nAktuálne funkcie:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nVybrané funkcie môžete zmeniť v časti Nastavenia > Dostupnosť."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Chcete zapnúť <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Pridržaním oboch klávesov hlasitosti na niekoľko sekúnd zapnete funkciu dostupnosti <xliff:g id="SERVICE">%1$s</xliff:g>. Môže sa tým zmeniť spôsob fungovania vášho zariadenia.\n\nTúto skratku môžete zmeniť na inú funkciu v časti Nastavenia > Dostupnosť."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Zapnúť"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nezapínať"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0079e38..85f1124 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ali želite povečati glasnost nad priporočeno raven?\n\nDolgotrajno poslušanje pri veliki glasnosti lahko poškoduje sluh."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Želite uporabljati bližnjico funkcij za ljudi s posebnimi potrebami?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Ko je bližnjica vklopljena, pritisnite gumba za glasnost in ju pridržite tri sekunde, če želite zagnati funkcijo za ljudi s posebnimi potrebami."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Želite vklopiti funkcije za ljudi s posebnimi potrebami?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Če za nekaj sekund pridržite obe tipki za glasnost, boste vklopili funkcije za ljudi s posebnimi potrebami. To lahko spremeni način delovanja naprave.\n\nTrenutne funkcije:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nIzbrane funkcije lahko spremenite v meniju »Nastavitve« > »Funkcije za ljudi s posebnimi potrebami«."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Želite vklopiti storitev <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Če za nekaj sekund pridržite obe tipki za glasnost, boste vklopili storitev <xliff:g id="SERVICE">%1$s</xliff:g>, ki je funkcija za ljudi s posebnimi potrebami. To lahko spremeni način delovanja naprave.\n\nTo bližnjico lahko v meniju »Nastavitve« > »Funkcije za ljudi s posebnimi potrebami« spremenite, da bo uporabljena za drugo funkcijo."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Vklopi"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ne vklopi"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index b29582c..4fd25bd 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Të ngrihet volumi mbi nivelin e rekomanduar?\n\nDëgjimi me volum të lartë për periudha të gjata mund të dëmtojë dëgjimin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Të përdoret shkurtorja e qasshmërisë?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kur shkurtorja është e aktivizuar, shtypja e të dy butonave për 3 sekonda do të nisë një funksion qasshmërie."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Të aktivizohen veçoritë e qasshmërisë?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Mbajtja shtypur e dy tasteve të volumit për pak sekonda aktivizon veçoritë e qasshmërisë. Kjo mund të ndryshojë mënyrën se si funksionon pajisja jote.\n\nVeçoritë aktuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nKe ndryshuar veçoritë e zgjedhura te Cilësimet > Qasshmëria."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Të aktivizohet <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Mbajtja shtypur e dy tasteve të volumit për pak sekonda aktivizon <xliff:g id="SERVICE">%1$s</xliff:g>, një veçori të qasshmërisë. Kjo mund të ndryshojë mënyrën se si funksionon pajisja jote.\n\nMund të ndryshosh këtë shkurtore te një veçori tjetër te Cilësimet > Qasshmëria."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivizo"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Mos e aktivizo"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f24e285..aa37ea0 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1644,10 +1644,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Желите да појачате звук изнад препорученог нивоа?\n\nСлушање гласне музике дуже време може да вам оштети слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Желите ли да користите пречицу за приступачност?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Када је пречица укључена, притисните оба дугмета за јачину звука да бисте покренули функцију приступачности."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Желите ли да укључите функције приступачности?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ако задржите оба тастера за јачину звука пар секунди, укључиће се функције приступачности. То може да промени начин рада уређаја.\n\nПостојеће функције:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nМожете да промените изабране функције у одељку Подешавања > Приступачност."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Желите ли да укључите услугу <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ако задржите оба тастера за јачину звука пар секунди, укључује се <xliff:g id="SERVICE">%1$s</xliff:g>, функција приступачности. То може да промени начин рада уређаја.\n\nМожете да промените функцију на коју се односи ова пречица у одељку Подешавања > Приступачност."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Укључи"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не укључуј"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 5c9ce2c..acea0d5 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Vill du höja volymen över den rekommenderade nivån?\n\nAtt lyssna med stark volym långa stunder åt gången kan skada hörseln."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Vill du använda Aktivera tillgänglighet snabbt?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"När kortkommandot har aktiverats startar du en tillgänglighetsfunktion genom att trycka ned båda volymknapparna i tre sekunder."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Vill du aktivera tillgänglighetsfunktionerna?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Om du trycker ned båda volymknapparna i ett par sekunder aktiveras tillgänglighetsfunktionerna. Det kan få enheten ett fungera annorlunda.\n\nAktuella funktioner:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kan ändra vilka funktioner som aktiveras under Inställningar > Tillgänglighet."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vill du aktivera <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Om du trycker ned båda volymknapparna i ett par sekunder aktiveras <xliff:g id="SERVICE">%1$s</xliff:g>, en tillgänglighetsfunktion. Det kan leda till att enheten fungerar annorlunda.\n\nDu kan ändra vilken funktion som ska aktiveras med genvägen under Inställningar > Tillgänglighet."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivera"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Aktivera inte"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 55db3eb..aa0c520 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ungependa kupandisha sauti zaidi ya kiwango kinachopendekezwa?\n\nKusikiliza kwa sauti ya juu kwa muda mrefu kunaweza kuharibu uwezo wako wa kusikia."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Ungependa kutumia njia ya mkato ya ufikivu?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Unapowasha kipengele cha njia ya mkato, hatua ya kubonyeza vitufe vyote viwili vya sauti kwa sekunde tatu itafungua kipengele cha ufikivu."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Ungependa kuwasha vipengele vya ufikivu?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Hatua ya kushikilia chini vitufe vyote viwili vya sauti kwa sekunde chache huwasha vipengele vya ufikivu. Huenda hatua hii ikabadilisha jinsi kifaa chako kinavyofanya kazi.\n\nVipengele vya sasa:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nUnaweza kubadilisha vipengele ulivyochagua katika Mipangilio > Ufikivu."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Ungependa kuwasha <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Hatua ya kushikilia chini vitufe vyote viwili vya sauti kwa sekunde chache huwasha <xliff:g id="SERVICE">%1$s</xliff:g>, kipengele cha ufikivu. Huenda hatua hii ikabadilisha jinsi kifaa chako kinavyofanya kazi.\n\nUnaweza kubadilisha njia hii ya mkato iwe kipengele kingine katika Mipangilio > Ufikivu."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Washa"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Usiwashe"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index be030d1..068c375 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"பரிந்துரைத்த அளவை விட ஒலியை அதிகரிக்கவா?\n\nநீண்ட நேரத்திற்கு அதிகளவில் ஒலி கேட்பது கேட்கும் திறனைப் பாதிக்கலாம்."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"அணுகல்தன்மை ஷார்ட்கட்டைப் பயன்படுத்தவா?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"ஷார்ட்கட் இயக்கத்தில் இருக்கும்போது ஒலியளவு பட்டன்கள் இரண்டையும் 3 வினாடிகளுக்கு அழுத்தினால் அணுகல்தன்மை அம்சம் இயக்கப்படும்."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"அணுகல்தன்மை அம்சங்களை ஆன் செய்யவா?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"இரண்டு ஒலியளவு விசைகளையும் சில விநாடிகள் பிடித்திருந்தால் அணுகல்தன்மை அம்சங்கள் ஆன் செய்யப்படும். இதனால் உங்கள் சாதனம் வேலை செய்யும் முறை மாறக்கூடும்.\n\nதற்போதைய அம்சங்கள்:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nதேர்ந்தெடுத்த அம்சங்களை அமைப்புகள் > அணுகல்தன்மைக்குச் சென்று உங்களால் மாற்ற முடியும்."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ஐ ஆன் செய்யவா?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"இரண்டு ஒலியளவு விசைகளையும் சில விநாடிகள் பிடித்திருப்பதால் அணுகல்தன்மை அம்சமான <xliff:g id="SERVICE">%1$s</xliff:g> ஆன் ஆகும். இதனால் உங்கள் சாதனம் வேலை செய்யும் முறை மாறக்கூடும்.\n\nஅமைப்புகள் > அணுகல்தன்மைக்குச் சென்று இந்த ஷார்ட்கட்டை வேறு அம்சத்திற்கு மாற்ற முடியும்."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ஆன் செய்"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ஆன் செய்யாதே"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 0966d23..6016082 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"వాల్యూమ్ను సిఫార్సు చేయబడిన స్థాయి కంటే ఎక్కువగా పెంచాలా?\n\nసుదీర్ఘ వ్యవధుల పాటు అధిక వాల్యూమ్లో వినడం వలన మీ వినికిడి శక్తి దెబ్బ తినవచ్చు."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"యాక్సెస్ సామర్థ్యం షార్ట్కట్ను ఉపయోగించాలా?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"షార్ట్కట్ ఆన్ చేసి ఉన్నప్పుడు, రెండు వాల్యూమ్ బటన్లను 3 సెకన్ల పాటు నొక్కి ఉంచితే యాక్సెస్ సౌలభ్య ఫీచర్ ప్రారంభం అవుతుంది."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"యాక్సెసిబిలిటీ ఫీచర్లను ఆన్ చేయాలా?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"రెండు వాల్యూమ్ కీలను కొంత సేపు నొక్కి పట్టుకుంటే యాక్సెసిబిలిటీ ఫీచర్లు ఆన్ అవుతాయి. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nప్రస్తుత ఫీచర్లు:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nఎంపిక చేసిన ఫీచర్లను మీరు సెట్టింగ్లు>యాక్సెసిబిలిటీలో మార్చవచ్చు."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> ఆన్ చేయాాలా?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"రెండు వాల్యూమ్ కీలను కొన్ని సెకన్ల పాటు నొక్కి పట్టుకోవడం ద్వారా యాక్సెసిబిలిటీ అయిన <xliff:g id="SERVICE">%1$s</xliff:g> ఆన్ అవుతుంది. ఇది మీ పరికరం పని చేసే విధానాన్ని మార్చవచ్చు.\n\nసెట్టింగ్లు > యాక్సెసిబిలిటీలో, వేరొక ఫీచర్ను ప్రారంభించేలా ఈ షార్ట్ కట్ను మీరు మార్చవచ్చు."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"ఆన్ చేయి"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ఆన్ చేయకండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7ff76ec..2feea14 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"นี่เป็นการเพิ่มระดับเสียงเกินระดับที่แนะนำ\n\nการฟังเสียงดังเป็นเวลานานอาจทำให้การได้ยินของคุณบกพร่องได้"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ใช้ทางลัดการช่วยเหลือพิเศษไหม"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"เมื่อทางลัดเปิดอยู่ การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มนาน 3 วินาทีจะเริ่มฟีเจอร์การช่วยเหลือพิเศษ"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ต้องการเปิดฟีเจอร์การช่วยเหลือพิเศษใช่ไหม"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 2-3 วินาทีจะเปิดฟีเจอร์การช่วยเหลือพิเศษ การดำเนินการนี้อาจเปลี่ยนแปลงลักษณะการทำงานของอุปกรณ์\n\nฟีเจอร์ปัจจุบัน:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nคุณจะเปลี่ยนฟีเจอร์ที่เลือกไว้ได้ในการตั้งค่า > การช่วยเหลือพิเศษ"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"ต้องการเปิด <xliff:g id="SERVICE">%1$s</xliff:g> ใช่ไหม"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"การกดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้ 2-3 วินาทีจะเปิด <xliff:g id="SERVICE">%1$s</xliff:g> ซึ่งเป็นฟีเจอร์การช่วยเหลือพิเศษ การดำเนินการนี้อาจเปลี่ยนแปลงลักษณะการทำงานของอุปกรณ์\n\nคุณแก้ไขทางลัดนี้ให้เปิดฟีเจอร์อื่นได้ในการตั้งค่า > การช่วยเหลือพิเศษ"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"เปิด"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"ไม่ต้องเปิด"</string>
@@ -2031,7 +2033,7 @@
<item quantity="other"><xliff:g id="FILE_NAME_2">%s</xliff:g> และอีก <xliff:g id="COUNT_3">%d</xliff:g> ไฟล์</item>
<item quantity="one"><xliff:g id="FILE_NAME_0">%s</xliff:g> และอีก <xliff:g id="COUNT_1">%d</xliff:g> ไฟล์</item>
</plurals>
- <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ไม่มีบุคคลที่แนะนำให้แชร์ด้วย"</string>
+ <string name="chooser_no_direct_share_targets" msgid="1511722103987329028">"ไม่พบใครที่แนะนำให้แชร์ด้วย"</string>
<string name="chooser_all_apps_button_label" msgid="3230427756238666328">"รายชื่อแอป"</string>
<string name="usb_device_resolve_prompt_warn" msgid="325871329788064199">"แอปนี้ไม่ได้รับอนุญาตให้บันทึกเสียงแต่จะบันทึกเสียงผ่านอุปกรณ์ USB นี้ได้"</string>
<string name="accessibility_system_action_home_label" msgid="3234748160850301870">"หน้าแรก"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 7d9c717..41ea057 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Lakasan ang volume nang lagpas sa inirerekomendang antas?\n\nMaaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Gagamitin ang Shortcut sa Accessibility?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kapag naka-on ang shortcut, magsisimula ang isang feature ng pagiging naa-access kapag pinindot ang parehong button ng volume."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"I-on ang mga feature ng accessibility?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Mao-on ang mga feature ng accessibility kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nMga kasalukuyang feature:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuwede mong baguhin ang mga napiling feature sa Mga Setting > Accessibility."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"I-on ang <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Mao-on ang feature ng accessibility na <xliff:g id="SERVICE">%1$s</xliff:g> kapag pinindot nang matagal ang parehong volume key nang ilang segundo. Posibleng mabago nito ang paggana ng iyong device.\n\nPuwede mong palitan ng ibang feature ang shortcut na ito sa Mga Setting > Accessibility."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"I-on"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Huwag i-on"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a658bf1..4513eb6 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ses seviyesi önerilen düzeyin üzerine yükseltilsin mi?\n\nUzun süre yüksek ses seviyesinde dinlemek işitme duyunuza zarar verebilir."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Erişilebilirlik Kısayolu Kullanılsın mı?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Kısayol açıkken ses düğmelerinin ikisini birden 3 saniyeliğine basılı tutmanız bir erişilebilirlik özelliğini başlatır."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Erişilebilirlik özellikleri etkinleştirilsin mi?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ses tuşlarının ikisini birden birkaç saniyeliğine basılı tutmak, erişilebilirlik özelliklerini açar. Bu, cihazınızın çalışma şeklini değiştirebilir.\n\nGeçerli özellikler:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nSeçilen özellikleri Ayarlar > Erişilebilirlik\'te değiştirebilirsiniz."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> etkinleştirilsin mi?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ses tuşlarının ikisini birden birkaç saniyeliğine basılı tutmak <xliff:g id="SERVICE">%1$s</xliff:g> erişilebilirlik özelliğini etkinleştirir. Bu, cihazınızın çalışma şeklini değiştirebilir.\n\nBu kısayolu, Ayarlar > Erişilebilirlik\'te başka bir özellikle değiştirebilirsiniz."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Etkinleştir"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Etkinleştirme"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 7e81784..0f5ce7e 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1666,10 +1666,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Збільшити гучність понад рекомендований рівень?\n\nЯкщо слухати надто гучну музику тривалий час, можна пошкодити слух."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Використовувати швидке ввімкнення?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Якщо цей засіб увімкнено, ви можете активувати спеціальні можливості, утримуючи обидві кнопки гучності протягом трьох секунд."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Увімкнути спеціальні можливості?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Якщо втримувати обидві клавіші гучності впродовж кількох секунд, вмикаються спеціальні можливості. Це впливає на роботу пристрою.\n\nПоточні функції:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nВибрані функції можна змінити в налаштуваннях у меню спеціальних можливостей."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Увімкнути <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Якщо втримувати обидві клавіші гучності впродовж кількох секунд, буде ввімкнено спеціальні можливості – <xliff:g id="SERVICE">%1$s</xliff:g>. Це може вплинути на роботу пристрою.\n\nДля цієї комбінації клавіш можна вибрати іншу функцію в меню \"Налаштування > Спеціальні можливості\"."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Увімкнути"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Не вмикати"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index b9b9c58..7720480 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"والیوم کو تجویز کردہ سطح سے زیادہ کریں؟\n\nزیادہ وقت تک اونچی آواز میں سننے سے آپ کی سماعت کو نقصان پہنچ سکتا ہے۔"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"ایکسیسبیلٹی شارٹ کٹ استعمال کریں؟"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"شارٹ کٹ آن ہونے پر، 3 سیکنڈ تک دونوں والیوم بٹنز کو دبانے سے ایک ایکسیسبیلٹی خصوصیت شروع ہو جائے گی۔"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"ایکسیسبیلٹی خصوصیات آن کریں؟َ"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"دونوں والیوم کی کلیدوں کو کچھ سیکنڈز تک دبائیں رکھنے سے ایکسیسبیلٹی خصوصیات آن ہو جاتی ہیں۔ اس سے آپ کے آلے کے کام کرنے کا طریقہ تبدیل ہو سکتا ہے۔\n\nموجودہ خصوصیات:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nآپ ترتیبات اور ایکسیسبیلٹی میں منتخب کردہ خصوصیات کو تبدیل کر سکتے ہیں۔"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> آن کریں؟"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"والیوم کی دونوں کلیدوں کو کچھ سیکنڈز تک دبائے رکھنے سے <xliff:g id="SERVICE">%1$s</xliff:g> ایکسیسبیلٹی خصوصیت آن ہو جاتی ہے۔ اس سے آپ کے آلے کے کام کرنے کا طریقہ تبدیل ہو سکتا ہے۔\n\nآپ ترتیبات اور ایکسیسبیلٹی میں دیگر خصوصیت کے لیے اس شارٹ کٹ کو تبدیل کر سکتے ہیں۔"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"آن کریں"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"آن نہ کریں"</string>
@@ -1829,8 +1831,7 @@
<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_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-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 5edd9dc..49caf1b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Tovush balandligi tavsiya etilgan darajadan ham yuqori qilinsinmi?\n\nUzoq vaqt davomida baland ovozda tinglash eshitish qobiliyatingizga salbiy ta’sir ko‘rsatishi mumkin."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Tezkor ishga tushirishdan foydalanilsinmi?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Maxsus imkoniyatlar funksiyasidan foydalanish uchun u yoniqligida ikkala tovush tugmasini 3 soniya bosib turing."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Maxsus imkoniyatlar yoqilsinmi?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Maxsus imkoniyatlarni yoqish uchun ikkala tovush tugmalarini bir necha soniya bosib turing. Qurilmangiz ishlashida oʻzgarish yuz berishi mumkin.\n\nJoriy funksiyalar:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nTanlangan funksiyalarni Sozlamalar ichidagi Maxsus imkoniyatlar ustiga bosib oʻzgartirishingiz mumkin."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"<xliff:g id="SERVICE">%1$s</xliff:g> yoqilsinmi?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"<xliff:g id="SERVICE">%1$s</xliff:g> funksiyasini yoqish uchun ikkala tovush tugmalarini bir necha soniya bosib turing. Qurilmangiz ishlashida oʻzgarish yuz berishi mumkin.\n\nBu tezkor tugmalarni boshqa funksiyaga Sozlamalar ichidagi Maxsus imkoniyatlar orqali tayinlash mumkin."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Yoqilsin"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Yoqilmasin"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index e3c7658..3b65cc1 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Bạn tăng âm lượng lên quá mức khuyên dùng?\n\nViệc nghe ở mức âm lượng cao trong thời gian dài có thể gây tổn thương thính giác của bạn."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Sử dụng phím tắt Hỗ trợ tiếp cận?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Khi phím tắt này đang bật, thao tác nhấn cả hai nút âm lượng trong 3 giây sẽ mở tính năng hỗ trợ tiếp cận."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Bật các tính năng hỗ trợ tiếp cận?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Thao tác nhấn và giữ cả hai phím âm lượng trong vài giây sẽ bật các tính năng hỗ trợ tiếp cận. Việc bật các tính năng này có thể thay đổi cách thiết bị của bạn hoạt động.\n\nCác tính năng hiện tại:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nBạn có thể thay đổi những tính năng đã chọn trong phần Cài đặt > Hỗ trợ tiếp cận."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Bật <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Thao tác nhấn và giữ cả hai phím âm lượng trong vài giây sẽ bật <xliff:g id="SERVICE">%1$s</xliff:g>, một tính năng hỗ trợ tiếp cận. Việc bật tính năng này có thể thay đổi cách thiết bị của bạn hoạt động.\n\nBạn có thể chuyển phím tắt này thành một tính năng khác trong phần Cài đặt > Hỗ trợ tiếp cận."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Bật"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Không bật"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index dee9d95..10472cb 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要将音量调高到建议的音量以上吗?\n\n长时间保持高音量可能会损伤听力。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用无障碍快捷方式吗?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"启用这项快捷方式后,同时按下两个音量按钮 3 秒钟即可启动无障碍功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要开启无障碍功能吗?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同时按住两个音量键几秒钟,即可开启无障碍功能。这样做可能会改变您设备的工作方式。\n\n当前功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n您可以在“设置”>“无障碍”中更改所选功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要开启<xliff:g id="SERVICE">%1$s</xliff:g>吗?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同时按住两个音量键几秒钟,即可开启<xliff:g id="SERVICE">%1$s</xliff:g>无障碍功能。这样做可能会改变您设备的工作方式。\n\n您可以在“设置”>“无障碍”中将此快捷方式更改为开启另一项功能。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"开启"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不开启"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 44ad863..455ae54 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要調高音量 (比建議的音量更大聲) 嗎?\n\n長時間聆聽高分貝音量可能會導致您的聽力受損。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用無障礙功能快速鍵嗎?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"啟用快速鍵後,同時按住音量按鈕 3 秒便可啟用無障礙功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要開啟無障礙功能嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同時按下兩個音量鍵幾秒,以開啟無障礙功能。這可能會變更裝置的運作。\n\n目前功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n您可在「設定」>「無障礙功能」中變更所選功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要啟用 <xliff:g id="SERVICE">%1$s</xliff:g> 嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同時按下兩個音量鍵幾秒,以開啟 <xliff:g id="SERVICE">%1$s</xliff:g> 無障礙功能。這可能會變更裝置的運作。\n\n您可在「設定」>「無障礙功能」中變更此快速鍵。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"開啟"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不要開啟"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8831bf0..5971d97 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"要調高音量,比建議的音量更大聲嗎?\n\n長時間聆聽高分貝音量可能會使你的聽力受損。"</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"要使用無障礙捷徑嗎?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"啟用捷徑功能,只要同時按下兩個音量按鈕 3 秒,就能啟動無障礙功能。"</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"要開啟無障礙功能嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"同時按住音量調高鍵和調低鍵數秒,即可開啟無障礙功能。這麼做可能會改變裝置的運作方式。\n\n目前的功能:\n<xliff:g id="SERVICE">%1$s</xliff:g>\n你可以在 [設定] > [無障礙設定] 中變更選取的功能。"</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"要開啟「<xliff:g id="SERVICE">%1$s</xliff:g>」嗎?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"同時按住音量調高鍵和調低鍵數秒,即可開啟「<xliff:g id="SERVICE">%1$s</xliff:g>」無障礙功能。這麼做可能會改變裝置的運作方式。\n\n你可以在 [設定] > [無障礙設定] 中變更這個快速鍵觸發的功能。"</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"開啟"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"不要開啟"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 5fb9fa6..4a1033c 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1622,10 +1622,12 @@
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Khuphukisa ivolumu ngaphezu kweleveli enconyiwe?\n\nUkulalela ngevolumu ephezulu izikhathi ezide kungahle kulimaze ukuzwa kwakho."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Sebenzisa isinqamuleli sokufinyelela?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Uma isinqamuleli sivuliwe, ukucindezela zombili izinkinobho zevolumu amasekhondi angu-3 kuzoqalisa isici sokufinyelela."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="8417489297036013065">"Uvula izici zokufinyelela?"</string>
+ <!-- no translation found for accessibility_shortcut_multiple_service_warning_title (3135860819356676426) -->
+ <skip />
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Ukubambela phansi bobabili okhiye bevolumu amasekhondi ambalwa kuvula izici zokufinyelela. Lokhu kungashintsha indlela idivayisi yakho esebenza ngayo.\n\nIzici zamanje:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nUngashintsha izici ezikhethiwe Kuzilungiselelo > Ukufinyeleleka."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="6935581470716541531">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="2819109500943271385">"Vula i-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <!-- no translation found for accessibility_shortcut_single_service_warning_title (1909518473488345266) -->
+ <skip />
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Ukubambela phansi bobabili okhiye bevolumu amasekhondi ambalwa kuvula i-<xliff:g id="SERVICE">%1$s</xliff:g>, eyisici sokufinyelela Lokhu kungashintsha indlela idivayisi yakho esebenza ngayo.\n\nUngashintshela lesi sinqamuleli kwesinye isici Kuzilungiselelo > Ukufinyeleleka."</string>
<string name="accessibility_shortcut_on" msgid="5463618449556111344">"Vula"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Ungavuli"</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 71cb2ac..56f18d5 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1599,6 +1599,7 @@
</activity>
<activity android:name="android.app.activity.ActivityThreadTest$TestActivity"
+ android:configChanges="screenLayout|screenSize|orientation|smallestScreenSize"
android:supportsPictureInPicture="true"
android:exported="true">
</activity>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 23534e2..8b25afb 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -69,6 +69,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Test for verifying {@link android.app.ActivityThread} class.
@@ -78,6 +79,7 @@
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ActivityThreadTest {
+ private static final int TIMEOUT_SEC = 10;
// The first sequence number to try with. Use a large number to avoid conflicts with the first a
// few sequence numbers the framework used to launch the test activity.
@@ -309,7 +311,7 @@
transaction.addCallback(ActivityConfigurationChangeItem.obtain(activityConfigPortrait));
appThread.scheduleTransaction(transaction);
- activity.mTestLatch.await();
+ activity.mTestLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
activity.mConfigLatch.countDown();
activity.mConfigLatch = null;
@@ -352,7 +354,7 @@
// Wait until the main thread is performing the configuration change for the configuration
// with sequence number BASE_SEQ + 1 before proceeding. This is to mimic the situation where
// the activity takes very long time to process configuration changes.
- activity.mTestLatch.await();
+ activity.mTestLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
config = new Configuration();
config.seq = BASE_SEQ + 2;
@@ -738,7 +740,7 @@
mTestLatch.countDown();
}
try {
- mConfigLatch.await();
+ mConfigLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
diff --git a/core/tests/coretests/src/android/view/InsetsFlagsTest.java b/core/tests/coretests/src/android/view/InsetsFlagsTest.java
deleted file mode 100644
index b4302e7..0000000
--- a/core/tests/coretests/src/android/view/InsetsFlagsTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-
-import static android.view.InsetsFlags.getAppearance;
-import static android.view.View.NAVIGATION_BAR_TRANSLUCENT;
-import static android.view.View.NAVIGATION_BAR_TRANSPARENT;
-import static android.view.View.STATUS_BAR_TRANSLUCENT;
-import static android.view.View.STATUS_BAR_TRANSPARENT;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
-import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
-import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
-import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
-
-import static org.junit.Assert.assertTrue;
-
-import android.platform.test.annotations.Presubmit;
-import android.view.WindowInsetsController.Appearance;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link InsetsFlags}.
- *
- * <p>Build/Install/Run:
- * atest FrameworksCoreTests:InsetsFlagsTest
- *
- * <p>This test class is a part of Window Manager Service tests and specified in
- * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
- */
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class InsetsFlagsTest {
-
- @Test
- public void testGetAppearance() {
- assertContainsAppearance(APPEARANCE_LOW_PROFILE_BARS, SYSTEM_UI_FLAG_LOW_PROFILE);
- assertContainsAppearance(APPEARANCE_LIGHT_STATUS_BARS, SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
- assertContainsAppearance(APPEARANCE_LIGHT_NAVIGATION_BARS,
- SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
- assertContainsAppearance(APPEARANCE_OPAQUE_STATUS_BARS,
- 0xffffffff & ~(STATUS_BAR_TRANSLUCENT | STATUS_BAR_TRANSPARENT));
- assertContainsAppearance(APPEARANCE_OPAQUE_NAVIGATION_BARS,
- 0xffffffff & ~(NAVIGATION_BAR_TRANSLUCENT | NAVIGATION_BAR_TRANSPARENT));
- }
-
- void assertContainsAppearance(@Appearance int appearance, int systemUiVisibility) {
- assertTrue((getAppearance(systemUiVisibility) & appearance) == appearance);
- }
-}
diff --git a/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java b/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
index ec8dfcb..91266f0 100644
--- a/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewProcessTextTest.java
@@ -16,20 +16,10 @@
package android.widget;
-import static android.widget.RichContentReceiver.SOURCE_PROCESS_TEXT;
-
import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.Activity;
import android.app.Instrumentation;
-import android.content.ClipData;
-import android.content.ClipDescription;
import android.content.Intent;
import android.text.Selection;
import android.text.Spannable;
@@ -44,10 +34,6 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mockito;
-
-import java.util.Set;
/**
* Tests for {@link Intent#ACTION_PROCESS_TEXT} functionality in {@link TextView}.
@@ -78,9 +64,6 @@
mTextView.setTextIsSelectable(true);
Selection.setSelection((Spannable) mTextView.getText(), 0, mTextView.getText().length());
- MockReceiverWrapper mockReceiverWrapper = new MockReceiverWrapper();
- mTextView.setRichContentReceiver(mockReceiverWrapper);
-
// We need to run this in the UI thread, as it will create a Toast.
mActivityRule.runOnUiThread(() -> {
triggerOnActivityResult(Activity.RESULT_OK, "Text is replaced.");
@@ -89,46 +72,6 @@
// This is a TextView, which can't be modified. Hence no change should have been made.
assertEquals(originalText, mTextView.getText().toString());
- verifyZeroInteractions(mockReceiverWrapper.mMock);
- }
-
- @Test
- public void testProcessTextActivityResultEditable_defaultRichContentReceiver()
- throws Throwable {
- mActivityRule.runOnUiThread(() -> mTextView = new EditText(mActivity));
- mInstrumentation.waitForIdleSync();
- CharSequence originalText = "This is some text.";
- mTextView.setText(originalText, BufferType.SPANNABLE);
- assertEquals(originalText, mTextView.getText().toString());
- mTextView.setTextIsSelectable(true);
- Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length());
-
- CharSequence newText = "Text is replaced.";
- triggerOnActivityResult(Activity.RESULT_OK, newText);
-
- assertEquals(newText, mTextView.getText().toString());
- }
-
- @Test
- public void testProcessTextActivityResultEditable_customRichContentReceiver() throws Throwable {
- mActivityRule.runOnUiThread(() -> mTextView = new EditText(mActivity));
- mInstrumentation.waitForIdleSync();
- CharSequence originalText = "This is some text.";
- mTextView.setText(originalText, BufferType.SPANNABLE);
- assertEquals(originalText, mTextView.getText().toString());
- mTextView.setTextIsSelectable(true);
- Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length());
-
- MockReceiverWrapper mockReceiverWrapper = new MockReceiverWrapper();
- mTextView.setRichContentReceiver(mockReceiverWrapper);
-
- CharSequence newText = "Text is replaced.";
- triggerOnActivityResult(Activity.RESULT_OK, newText);
-
- ClipData expectedClip = ClipData.newPlainText("", newText);
- verify(mockReceiverWrapper.mMock, times(1)).onReceive(
- eq(mTextView), clipEq(expectedClip), eq(SOURCE_PROCESS_TEXT), eq(0));
- verifyNoMoreInteractions(mockReceiverWrapper.mMock);
}
@Test
@@ -141,14 +84,10 @@
mTextView.setTextIsSelectable(true);
Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length());
- MockReceiverWrapper mockReceiverWrapper = new MockReceiverWrapper();
- mTextView.setRichContentReceiver(mockReceiverWrapper);
-
CharSequence newText = "Text is replaced.";
triggerOnActivityResult(Activity.RESULT_CANCELED, newText);
assertEquals(originalText, mTextView.getText().toString());
- verifyZeroInteractions(mockReceiverWrapper.mMock);
}
@Test
@@ -161,13 +100,9 @@
mTextView.setTextIsSelectable(true);
Selection.setSelection(((EditText) mTextView).getText(), 0, mTextView.getText().length());
- MockReceiverWrapper mockReceiverWrapper = new MockReceiverWrapper();
- mTextView.setRichContentReceiver(mockReceiverWrapper);
-
mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, null);
assertEquals(originalText, mTextView.getText().toString());
- verifyZeroInteractions(mockReceiverWrapper.mMock);
}
private void triggerOnActivityResult(int resultCode, CharSequence replacementText) {
@@ -175,55 +110,4 @@
data.putExtra(Intent.EXTRA_PROCESS_TEXT, replacementText);
mTextView.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, resultCode, data);
}
-
- // This wrapper is used so that we only mock and verify the public callback methods. In addition
- // to the public methods, the RichContentReceiver interface has some hidden default methods;
- // we don't want to mock or assert calls to these helper functions (they are an implementation
- // detail).
- private static class MockReceiverWrapper implements RichContentReceiver<TextView> {
- private final RichContentReceiver<TextView> mMock;
-
- @SuppressWarnings("unchecked")
- MockReceiverWrapper() {
- this.mMock = Mockito.mock(RichContentReceiver.class);
- }
-
- public RichContentReceiver<TextView> getMock() {
- return mMock;
- }
-
- @Override
- public boolean onReceive(TextView view, ClipData clip, @Source int source,
- @Flags int flags) {
- return mMock.onReceive(view, clip, source, flags);
- }
-
- @Override
- public Set<String> getSupportedMimeTypes() {
- return mMock.getSupportedMimeTypes();
- }
- }
-
- private static ClipData clipEq(ClipData expected) {
- return argThat(new ClipDataArgumentMatcher(expected));
- }
-
- private static class ClipDataArgumentMatcher implements ArgumentMatcher<ClipData> {
- private final ClipData mExpected;
-
- private ClipDataArgumentMatcher(ClipData expected) {
- this.mExpected = expected;
- }
-
- @Override
- public boolean matches(ClipData actual) {
- ClipDescription actualDesc = actual.getDescription();
- ClipDescription expectedDesc = mExpected.getDescription();
- return expectedDesc.getLabel().equals(actualDesc.getLabel())
- && actualDesc.getMimeTypeCount() == 1
- && expectedDesc.getMimeType(0).equals(actualDesc.getMimeType(0))
- && actual.getItemCount() == 1
- && mExpected.getItemAt(0).getText().equals(actual.getItemAt(0).getText());
- }
- }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
new file mode 100644
index 0000000..403c1c2
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelSingleProcessCpuThreadReaderTest {
+
+ private File mProcDirectory;
+
+ @Before
+ public void setUp() {
+ Context context = InstrumentationRegistry.getContext();
+ mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mProcDirectory);
+ }
+
+ @Test
+ public void getThreadCpuUsage() throws IOException {
+ setupDirectory(42,
+ new int[] {42, 1, 2, 3},
+ new int[] {1000, 2000},
+ // Units are 10ms aka 10000Us
+ new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 400}},
+ new int[] {1400, 1500});
+
+ KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(42,
+ mProcDirectory.toPath());
+ KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
+ reader.getProcessCpuUsage();
+ assertThat(processCpuUsage.cpuTimeMillis).isEqualTo(29000);
+ List<KernelSingleProcessCpuThreadReader.ThreadCpuUsage> threadCpuUsage =
+ processCpuUsage.threadCpuUsages;
+ threadCpuUsage.sort(Comparator.comparingInt(o -> o.threadId));
+ assertThat(threadCpuUsage).hasSize(4);
+ assertThat(threadCpuUsage.get(0).threadId).isEqualTo(1);
+ assertThat(threadCpuUsage.get(0).usageTimesMillis).isEqualTo(new long[]{0, 2000});
+ assertThat(threadCpuUsage.get(1).threadId).isEqualTo(2);
+ assertThat(threadCpuUsage.get(1).usageTimesMillis).isEqualTo(new long[]{1000, 3000});
+ assertThat(threadCpuUsage.get(2).threadId).isEqualTo(3);
+ assertThat(threadCpuUsage.get(2).usageTimesMillis).isEqualTo(new long[]{0, 4000});
+ assertThat(threadCpuUsage.get(3).threadId).isEqualTo(42);
+ assertThat(threadCpuUsage.get(3).usageTimesMillis).isEqualTo(new long[]{1000, 2000});
+ }
+
+ @Test
+ public void getCpuFrequencyCount() throws IOException {
+ setupDirectory(13,
+ new int[] {13},
+ new int[] {1000, 2000, 3000},
+ new int[][] {{100, 200, 300}},
+ new int[] {14, 15});
+
+ KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(13,
+ mProcDirectory.toPath());
+ int cpuFrequencyCount = reader.getCpuFrequencyCount();
+ assertThat(cpuFrequencyCount).isEqualTo(3);
+ }
+
+ private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies,
+ int[][] threadCpuTimes, int[] processCpuTimes)
+ throws IOException {
+
+ assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
+
+ try (OutputStream timeInStateStream =
+ Files.newOutputStream(
+ mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
+ for (int i = 0; i < cpuFrequencies.length; i++) {
+ final String line = cpuFrequencies[i] + " 0\n";
+ timeInStateStream.write(line.getBytes());
+ }
+ }
+
+ Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
+
+ // Make /proc/$PID
+ assertTrue(processPath.toFile().mkdirs());
+
+ // Write /proc/$PID/stat. Only the fields 14-17 matter.
+ try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
+ timeInStateStream.write(
+ (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
+ + processCpuTimes[0] + " "
+ + processCpuTimes[1] + " "
+ + "16 17 18 19 20 ...").getBytes());
+ }
+
+ // Make /proc/$PID/task
+ final Path selfThreadsPath = processPath.resolve("task");
+ assertTrue(selfThreadsPath.toFile().mkdirs());
+
+ // Make thread directories
+ for (int i = 0; i < threadIds.length; i++) {
+ // Make /proc/$PID/task/$TID
+ final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
+ assertTrue(threadPath.toFile().mkdirs());
+
+ // Make /proc/$PID/task/$TID/time_in_state
+ try (OutputStream timeInStateStream =
+ Files.newOutputStream(threadPath.resolve("time_in_state"))) {
+ for (int j = 0; j < cpuFrequencies.length; j++) {
+ final String line = cpuFrequencies[j] + " " + threadCpuTimes[i][j] + "\n";
+ timeInStateStream.write(line.getBytes());
+ }
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
index 10ba548..121c637 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
@@ -55,82 +55,107 @@
@Test
public void testReaderDelta_firstTime() throws IOException {
- int uid = 42;
+ int pid = 42;
setupDirectory(
- mProcDirectory.toPath().resolve(String.valueOf(uid)),
- new int[]{42, 1, 2, 3},
- new int[]{1000, 2000},
+ pid,
+ new int[] {42, 1, 2, 3},
+ new int[] {1000, 2000},
// Units are 10ms aka 10000Us
- new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+ new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
+ new int[] {1400, 1500});
SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
- mProcDirectory.toPath(), uid);
- reader.setBinderThreadNativeTids(new int[]{1, 3});
+ mProcDirectory.toPath(), pid);
+ reader.setBinderThreadNativeTids(new int[] {1, 3});
SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
reader.readDelta();
- assertArrayEquals(new long[]{100 * 10000, 1100 * 10000},
+ assertArrayEquals(new long[] {100 * 10000, 1100 * 10000},
systemServiceCpuThreadTimes.threadCpuTimesUs);
- assertArrayEquals(new long[]{0, 600 * 10000},
+ assertArrayEquals(new long[] {0, 600 * 10000},
systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
}
@Test
public void testReaderDelta_nextTime() throws IOException {
- int uid = 42;
+ int pid = 42;
setupDirectory(
- mProcDirectory.toPath().resolve(String.valueOf(uid)),
- new int[]{42, 1, 2, 3},
- new int[]{1000, 2000},
- new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+ pid,
+ new int[] {42, 1, 2, 3},
+ new int[] {1000, 2000},
+ new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
+ new int[] {1400, 1500});
SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
- mProcDirectory.toPath(), uid);
- reader.setBinderThreadNativeTids(new int[]{1, 3});
+ mProcDirectory.toPath(), pid);
+ reader.setBinderThreadNativeTids(new int[] {1, 3});
// First time, populate "last" snapshot
reader.readDelta();
FileUtils.deleteContents(mProcDirectory);
setupDirectory(
- mProcDirectory.toPath().resolve(String.valueOf(uid)),
- new int[]{42, 1, 2, 3},
- new int[]{1000, 2000},
- new int[][]{{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}});
+ pid,
+ new int[] {42, 1, 2, 3},
+ new int[] {1000, 2000},
+ new int[][] {{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}},
+ new int[] {2400, 2500});
// Second time, get the actual delta
SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
reader.readDelta();
- assertArrayEquals(new long[]{3100 * 10000, 2500 * 10000},
+ assertArrayEquals(new long[] {3100 * 10000, 2500 * 10000},
systemServiceCpuThreadTimes.threadCpuTimesUs);
- assertArrayEquals(new long[]{1800 * 10000, 1400 * 10000},
+ assertArrayEquals(new long[] {1800 * 10000, 1400 * 10000},
systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
}
- private void setupDirectory(Path processPath, int[] threadIds, int[] cpuFrequencies,
- int[][] cpuTimes) throws IOException {
+ private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, int[][] cpuTimes,
+ int[] processCpuTimes)
+ throws IOException {
+
+ assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
+
+ try (OutputStream timeInStateStream =
+ Files.newOutputStream(
+ mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
+ for (int i = 0; i < cpuFrequencies.length; i++) {
+ final String line = cpuFrequencies[i] + " 0\n";
+ timeInStateStream.write(line.getBytes());
+ }
+ }
+
+ Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
// Make /proc/$PID
assertTrue(processPath.toFile().mkdirs());
+ // Write /proc/$PID/stat. Only the fields 14-17 matter.
+ try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
+ timeInStateStream.write(
+ (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
+ + processCpuTimes[0] + " "
+ + processCpuTimes[1] + " "
+ + "16 17 18 19 20 ...").getBytes());
+ }
+
// Make /proc/$PID/task
final Path selfThreadsPath = processPath.resolve("task");
assertTrue(selfThreadsPath.toFile().mkdirs());
- // Make thread directories in reverse order, as they are read in order of creation by
- // CpuThreadProcReader
+ // Make thread directories
for (int i = 0; i < threadIds.length; i++) {
// Make /proc/$PID/task/$TID
final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
assertTrue(threadPath.toFile().mkdirs());
// Make /proc/$PID/task/$TID/time_in_state
- final OutputStream timeInStateStream =
- Files.newOutputStream(threadPath.resolve("time_in_state"));
- for (int j = 0; j < cpuFrequencies.length; j++) {
- final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
- timeInStateStream.write(line.getBytes());
+ try (OutputStream timeInStateStream =
+ Files.newOutputStream(threadPath.resolve("time_in_state"))) {
+ for (int j = 0; j < cpuFrequencies.length; j++) {
+ final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
+ timeInStateStream.write(line.getBytes());
+ }
}
- timeInStateStream.close();
}
}
}
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 2eee140..37e1efd 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -43,18 +43,18 @@
private PowerProfile mProfile;
private MockBatteryStatsImpl mMockBatteryStats;
private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
- private MockServerCpuThreadReader mMockServerCpuThreadReader;
+ private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
private SystemServicePowerCalculator mSystemServicePowerCalculator;
@Before
public void setUp() throws IOException {
Context context = InstrumentationRegistry.getContext();
mProfile = new PowerProfile(context, true /* forTest */);
- mMockServerCpuThreadReader = new MockServerCpuThreadReader();
+ mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader();
mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks())
.setPowerProfile(mProfile)
- .setSystemServerCpuThreadReader(mMockServerCpuThreadReader)
+ .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
.setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
.setUserInfoProvider(new MockUserInfoProvider());
mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
@@ -65,12 +65,13 @@
@Test
public void testCalculateApp() {
// Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
- mMockServerCpuThreadReader.setThreadTimes(
- new long[]{30000, 40000, 50000, 60000, 70000, 80000, 90000},
- new long[]{20000, 30000, 40000, 50000, 60000, 70000, 80000});
+ mMockSystemServerCpuThreadReader.setCpuTimes(
+ 210000,
+ new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000},
+ new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000});
mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
- new long[]{10000, 20000, 30000, 40000, 50000, 60000, 70000}
+ new long[] {10000, 20000, 30000, 40000, 50000, 60000, 70000}
);
mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false);
@@ -106,13 +107,13 @@
mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0);
mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0,
BatteryStats.STATS_SINCE_CHARGED);
- assertEquals(0.27162, app1.systemServiceCpuPowerMah, 0.00001);
+ assertEquals(0.00018958, app1.systemServiceCpuPowerMah, 0.0000001);
BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP,
mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0);
mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0,
BatteryStats.STATS_SINCE_CHARGED);
- assertEquals(2.44458, app2.systemServiceCpuPowerMah, 0.00001);
+ assertEquals(0.00170625, app2.systemServiceCpuPowerMah, 0.0000001);
}
private static class MockKernelCpuUidFreqTimeReader extends
@@ -140,14 +141,16 @@
}
}
- private static class MockServerCpuThreadReader extends SystemServerCpuThreadReader {
+ private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader {
private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
- MockServerCpuThreadReader() {
+ MockSystemServerCpuThreadReader() {
super(null);
}
- public void setThreadTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
+ public void setCpuTimes(long processCpuTimeUs, long[] threadCpuTimesUs,
+ long[] binderThreadCpuTimesUs) {
+ mThreadTimes.processCpuTimeUs = processCpuTimeUs;
mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 86e7adf..9eaeed1 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1801,6 +1801,12 @@
"group": "WM_DEBUG_FOCUS_LIGHT",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
+ "625447638": {
+ "message": "Resize reasons for w=%s: %s configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_RESIZE",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"628276090": {
"message": "Delaying app transition for screen rotation animation to finish",
"level": "VERBOSE",
@@ -2113,12 +2119,6 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
},
- "1160771501": {
- "message": "Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
- "level": "VERBOSE",
- "group": "WM_DEBUG_RESIZE",
- "at": "com\/android\/server\/wm\/WindowState.java"
- },
"1166381079": {
"message": "Execute app transition: %s, displayId: %d Callers=%s",
"level": "WARN",
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 0504cdc..bda84dd 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -283,10 +283,30 @@
<font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font>
</family>
<family lang="und-Ethi">
- <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
- <font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
- <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.otf</font>
- <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Bold.otf</font>
+ <font weight="400" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal">NotoSansEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
+ <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="400" />
+ </font>
+ <font weight="500" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="500" />
+ </font>
+ <font weight="600" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="600" />
+ </font>
+ <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-VF.ttf
+ <axis tag="wght" stylevalue="700" />
+ </font>
</family>
<family lang="und-Hebr">
<font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
new file mode 100644
index 0000000..9fc0c8e
--- /dev/null
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.NonNull;
+import android.graphics.Shader.TileMode;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Intermediate rendering step used to render drawing commands with a corresponding
+ * visual effect
+ *
+ * @hide
+ */
+public final class RenderEffect {
+
+ private static class RenderEffectHolder {
+ public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ RenderEffect.class.getClassLoader(), nativeGetFinalizer());
+ }
+
+ /**
+ * Create a {@link RenderEffect} instance that will offset the drawing content
+ * by the provided x and y offset.
+ * @param offsetX offset along the x axis in pixels
+ * @param offsetY offset along the y axis in pixels
+ */
+ @NonNull
+ public static RenderEffect createOffsetEffect(float offsetX, float offsetY) {
+ return new RenderEffect(nativeCreateOffsetEffect(offsetX, offsetY, 0));
+ }
+
+ /**
+ * Create a {@link RenderEffect} instance with the provided x and y offset
+ * @param offsetX offset along the x axis in pixels
+ * @param offsetY offset along the y axis in pixels
+ * @param input target RenderEffect used to render in the offset coordinates.
+ */
+ @NonNull
+ public static RenderEffect createOffsetEffect(
+ float offsetX,
+ float offsetY,
+ @NonNull RenderEffect input
+ ) {
+ return new RenderEffect(nativeCreateOffsetEffect(
+ offsetX,
+ offsetY,
+ input.getNativeInstance()
+ )
+ );
+ }
+
+ /**
+ * Create a {@link RenderEffect} that blurs the contents of the optional input RenderEffect
+ * with the specified radius along the x and y axis. If no input RenderEffect is provided
+ * then all drawing commands issued with a {@link android.graphics.RenderNode} that this
+ * RenderEffect 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 inputEffect Input RenderEffect that provides the content to be blurred, can be null
+ * to indicate that the drawing commands on the RenderNode are to be
+ * blurred instead of the input RenderEffect
+ * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
+ */
+ @NonNull
+ public static RenderEffect createBlurEffect(
+ float radiusX,
+ float radiusY,
+ @NonNull RenderEffect inputEffect,
+ @NonNull TileMode edgeTreatment
+ ) {
+ long nativeInputEffect = inputEffect != null ? inputEffect.mNativeRenderEffect : 0;
+ return new RenderEffect(
+ nativeCreateBlurEffect(
+ radiusX,
+ radiusY,
+ nativeInputEffect,
+ edgeTreatment.nativeInt
+ )
+ );
+ }
+
+ /**
+ * Create a {@link RenderEffect} that blurs the contents of the
+ * {@link android.graphics.RenderNode} that this RenderEffect is installed on with the
+ * specified radius along hte x and y axis.
+ * @param radiusX Radius of blur along the X axis
+ * @param radiusY Radius of blur along the Y axis
+ * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
+ */
+ @NonNull
+ public static RenderEffect createBlurEffect(
+ float radiusX,
+ float radiusY,
+ @NonNull TileMode edgeTreatment
+ ) {
+ return new RenderEffect(
+ nativeCreateBlurEffect(
+ radiusX,
+ radiusY,
+ 0,
+ edgeTreatment.nativeInt
+ )
+ );
+ }
+
+ private final long mNativeRenderEffect;
+
+ /* only constructed from static factory methods */
+ private RenderEffect(long nativeRenderEffect) {
+ mNativeRenderEffect = nativeRenderEffect;
+ RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation(
+ this, mNativeRenderEffect);
+ }
+
+ /**
+ * Obtain the pointer to the underlying RenderEffect to be configured
+ * on a RenderNode object via {@link RenderNode#setRenderEffect(RenderEffect)}
+ */
+ /* package */ long getNativeInstance() {
+ return mNativeRenderEffect;
+ }
+
+ private static native long nativeCreateOffsetEffect(
+ float offsetX, float offsetY, long nativeInput);
+ private static native long nativeCreateBlurEffect(
+ float radiusX, float radiusY, long nativeInput, int edgeTreatment);
+ private static native long nativeGetFinalizer();
+}
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 8aacbc7..d812c1a 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -850,6 +850,23 @@
}
/**
+ * Configure the {@link android.graphics.RenderEffect} to apply to this RenderNode. This
+ * will apply a visual effect to the end result of the contents of this RenderNode before
+ * it is drawn into the destination. For example if
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
+ * is provided, the contents will be drawn in a separate layer, then this layer will
+ * be blurred when this RenderNode is drawn into the destination.
+ * @param renderEffect to be applied to the RenderNode. Passing null clears all previously
+ * configured RenderEffects
+ *
+ * @hide
+ */
+ public void setRenderEffect(@Nullable RenderEffect renderEffect) {
+ nSetRenderEffect(mNativeRenderNode,
+ renderEffect != null ? renderEffect.getNativeInstance() : 0);
+ }
+
+ /**
* Returns the translucency level of this display list.
*
* @return A value between 0.0f and 1.0f
@@ -1655,6 +1672,9 @@
private static native boolean nSetAlpha(long renderNode, float alpha);
@CriticalNative
+ private static native void nSetRenderEffect(long renderNode, long renderEffect);
+
+ @CriticalNative
private static native boolean nSetHasOverlappingRendering(long renderNode,
boolean hasOverlappingRendering);
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index cbae675..2a52ce9 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -26,8 +26,11 @@
import android.graphics.RectF;
import android.os.LocaleList;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import android.util.LongSparseArray;
import android.util.TypedValue;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import dalvik.annotation.optimization.CriticalNative;
@@ -40,6 +43,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
@@ -56,6 +60,15 @@
private static final int STYLE_ITALIC = 1;
private static final int STYLE_NORMAL = 0;
+ private static final Object MAP_LOCK = new Object();
+ // We need to have mapping from native ptr to Font object for later accessing from TextShape
+ // result since Typeface doesn't have reference to Font object and it is not always created from
+ // Font object. Sometimes Typeface is created in native layer only and there might not be Font
+ // object in Java layer. So, if not found in this cache, create new Font object for API user.
+ @GuardedBy("MAP_LOCK")
+ private static final LongSparseArray<WeakReference<Font>> FONT_PTR_MAP =
+ new LongSparseArray<>();
+
/**
* A builder class for creating new Font.
*/
@@ -501,6 +514,10 @@
mTtcIndex = ttcIndex;
mAxes = axes;
mLocaleList = localeList;
+
+ synchronized (MAP_LOCK) {
+ FONT_PTR_MAP.append(mNativePtr, new WeakReference<>(this));
+ }
}
/**
@@ -637,6 +654,63 @@
+ "}";
}
+ /**
+ * Lookup Font object from native pointer or create new one if not found.
+ * @hide
+ */
+ public static Font findOrCreateFontFromNativePtr(long ptr) {
+ // First, lookup from known mapps.
+ synchronized (MAP_LOCK) {
+ WeakReference<Font> fontRef = FONT_PTR_MAP.get(ptr);
+ if (fontRef != null) {
+ Font font = fontRef.get();
+ if (font != null) {
+ return font;
+ }
+ }
+
+ // If not found, create Font object from native object for Java API users.
+ ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
+ long packed = nGetFontInfo(ptr);
+ int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
+ boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
+ int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
+ int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
+ FontVariationAxis[] axes = new FontVariationAxis[axisCount];
+ char[] charBuffer = new char[4];
+ for (int i = 0; i < axisCount; ++i) {
+ long packedAxis = nGetAxisInfo(ptr, i);
+ float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
+ charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
+ charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
+ charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
+ charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
+ axes[i] = new FontVariationAxis(new String(charBuffer), value);
+ }
+ Font.Builder builder = new Font.Builder(buffer)
+ .setWeight(weight)
+ .setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
+ .setTtcIndex(ttcIndex)
+ .setFontVariationSettings(axes);
+
+ Font newFont = null;
+ try {
+ newFont = builder.build();
+ FONT_PTR_MAP.append(ptr, new WeakReference<>(newFont));
+ } catch (IOException e) {
+ // This must not happen since the buffer was already created once.
+ Log.e("Font", "Failed to create font object from existing buffer.", e);
+ }
+ return newFont;
+ }
+ }
+
+ @CriticalNative
+ private static native long nGetFontInfo(long ptr);
+
+ @CriticalNative
+ private static native long nGetAxisInfo(long ptr, int i);
+
@FastNative
private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
diff --git a/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
new file mode 100644
index 0000000..5655e7f
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/NativeFontBufferHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.fonts;
+
+import android.annotation.NonNull;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.nio.ByteBuffer;
+
+/**
+ * This is a helper class for showing native allocated buffer in Java API.
+ *
+ * @hide
+ */
+public class NativeFontBufferHelper {
+ private NativeFontBufferHelper() {}
+
+ private static final NativeAllocationRegistry REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ ByteBuffer.class.getClassLoader(), nGetReleaseFunc());
+
+ /**
+ * Wrap native buffer with ByteBuffer with adding reference to it.
+ */
+ public static @NonNull ByteBuffer refByteBuffer(long fontPtr) {
+ long refPtr = nRefFontBuffer(fontPtr);
+ ByteBuffer buffer = nWrapByteBuffer(refPtr);
+
+ // Releasing native object so that decreasing shared pointer ref count when the byte buffer
+ // is GCed.
+ REGISTRY.registerNativeAllocation(buffer, refPtr);
+
+ return buffer;
+ }
+
+ @CriticalNative
+ private static native long nRefFontBuffer(long fontPtr);
+
+ @FastNative
+ private static native ByteBuffer nWrapByteBuffer(long refPtr);
+
+ @CriticalNative
+ private static native long nGetReleaseFunc();
+}
diff --git a/graphics/java/android/graphics/text/GlyphStyle.java b/graphics/java/android/graphics/text/GlyphStyle.java
new file mode 100644
index 0000000..cc8c4d2
--- /dev/null
+++ b/graphics/java/android/graphics/text/GlyphStyle.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
+
+import android.annotation.ColorInt;
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+
+import java.util.Objects;
+
+/**
+ * Represents subset of Paint parameters such as font size, scaleX that is used to draw a glyph.
+ *
+ * Glyph is a most primitive unit of text drawing.
+ *
+ */
+public class GlyphStyle {
+ private @ColorInt int mColor;
+ private float mFontSize;
+ private float mScaleX;
+ private float mSkewX;
+ private int mFlags;
+
+ /**
+ * @param color a color.
+ * @param fontSize a font size in pixels.
+ * @param scaleX a horizontal scale factor.
+ * @param skewX a horizontal skew factor
+ * @param flags paint flags
+ *
+ * @see Paint#getFlags()
+ * @see Paint#setFlags(int)
+ */
+ public GlyphStyle(
+ @ColorInt int color,
+ @FloatRange(from = 0) float fontSize,
+ @FloatRange(from = 0) float scaleX,
+ @FloatRange(from = 0) float skewX,
+ int flags) {
+ mColor = color;
+ mFontSize = fontSize;
+ mScaleX = scaleX;
+ mSkewX = skewX;
+ mFlags = flags;
+ }
+
+ /**
+ * Create glyph style from Paint
+ *
+ * @param paint a paint
+ */
+ public GlyphStyle(@NonNull Paint paint) {
+ setFromPaint(paint);
+ }
+
+ /**
+ * Gets the color.
+ *
+ * @return a color
+ * @see Paint#getColor()
+ * @see Paint#setColor(int)
+ */
+ public @ColorInt int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Sets the color.
+ *
+ * @param color a color
+ * @see Paint#getColor()
+ * @see Paint#setColor(int)
+ */
+ public void setColor(@ColorInt int color) {
+ mColor = color;
+ }
+
+ /**
+ * Gets the font size in pixels.
+ *
+ * @return font size
+ * @see Paint#getTextSize()
+ * @see Paint#setTextSize(float)
+ */
+ public @FloatRange(from = 0) float getFontSize() {
+ return mFontSize;
+ }
+
+ /**
+ * Sets the font size in pixels.
+ *
+ * @param fontSize font size in pixel
+ * @see Paint#getTextSize()
+ * @see Paint#setTextSize(float)
+ */
+ public void setFontSize(@FloatRange(from = 0) float fontSize) {
+ mFontSize = fontSize;
+ }
+
+ /**
+ * Return the horizontal scale factor
+ *
+ * @return a horizontal scale factor
+ * @see Paint#getTextScaleX()
+ * @see Paint#setTextScaleX(float)
+ */
+ public @FloatRange(from = 0) float getScaleX() {
+ return mScaleX;
+ }
+
+ /**
+ * Set the horizontal scale factor
+ *
+ * @param scaleX a horizontal scale factor
+ * @see Paint#getTextScaleX()
+ * @see Paint#setTextScaleX(float)
+ */
+ public void setScaleX(@FloatRange(from = 0) float scaleX) {
+ mScaleX = scaleX;
+ }
+
+ /**
+ * Return the horizontal skew factor
+ *
+ * @return a horizontal skew factor
+ * @see Paint#getTextSkewX()
+ * @see Paint#setTextSkewX(float)
+ */
+ public @FloatRange(from = 0) float getSkewX() {
+ return mSkewX;
+ }
+
+ /**
+ * Set the horizontal skew factor
+ *
+ * @param skewX a horizontal skew factor
+ * @see Paint#getTextSkewX()
+ * @see Paint#setTextSkewX(float)
+ */
+ public void setSkewX(@FloatRange(from = 0) float skewX) {
+ mSkewX = skewX;
+ }
+
+ /**
+ * Returns the Paint flags.
+ *
+ * @return a paint flags
+ * @see Paint#getFlags()
+ * @see Paint#setFlags(int)
+ */
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Set the Paint flags.
+ *
+ * @param flags a paint flags
+ * @see Paint#getFlags()
+ * @see Paint#setFlags(int)
+ */
+ public void setFlags(int flags) {
+ mFlags = flags;
+ }
+
+ /**
+ * Applies glyph style to the paint object.
+ *
+ * @param paint a paint object
+ */
+ public void applyToPaint(@NonNull Paint paint) {
+ paint.setColor(mColor);
+ paint.setTextSize(mFontSize);
+ paint.setTextScaleX(mScaleX);
+ paint.setTextSkewX(mSkewX);
+ paint.setFlags(mFlags);
+ }
+
+ /**
+ * Copy parameters from a Paint object.
+ *
+ * @param paint a paint object
+ */
+ public void setFromPaint(@NonNull Paint paint) {
+ mColor = paint.getColor();
+ mFontSize = paint.getTextSize();
+ mScaleX = paint.getTextScaleX();
+ mSkewX = paint.getTextSkewX();
+ mFlags = paint.getFlags();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof GlyphStyle)) return false;
+ GlyphStyle that = (GlyphStyle) o;
+ return that.mColor == mColor
+ && Float.compare(that.mFontSize, mFontSize) == 0
+ && Float.compare(that.mScaleX, mScaleX) == 0
+ && Float.compare(that.mSkewX, mSkewX) == 0
+ && mFlags == that.mFlags;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mColor, mFontSize, mScaleX, mSkewX, mFlags);
+ }
+
+ @Override
+ public String toString() {
+ return "GlyphStyle{"
+ + "mColor=" + mColor
+ + ", mFontSize=" + mFontSize
+ + ", mScaleX=" + mScaleX
+ + ", mSkewX=" + mSkewX
+ + ", mFlags=" + mFlags
+ + '}';
+ }
+}
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
new file mode 100644
index 0000000..7364d54
--- /dev/null
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.graphics.fonts.Font;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+/**
+ * Text shaping result object for single style text.
+ *
+ * You can get text shaping result by
+ * {@link TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and
+ * {@link TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}.
+ *
+ * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ */
+public final class PositionedGlyphs {
+ private static final NativeAllocationRegistry REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ Typeface.class.getClassLoader(), nReleaseFunc());
+
+ private final long mLayoutPtr;
+ private final float mXOffset;
+ private final float mYOffset;
+ private final GlyphStyle mGlyphStyle;
+ private final ArrayList<Font> mFonts;
+
+ /**
+ * Returns the total amount of advance consumed by this positioned glyphs.
+ *
+ * The advance is an amount of width consumed by the glyph. The total amount of advance is
+ * a total amount of advance consumed by this series of glyphs. In other words, if another
+ * glyph is placed next to this series of glyphs, it's X offset should be shifted this amount
+ * of width.
+ *
+ * @return total amount of advance
+ */
+ public float getTotalAdvance() {
+ return nGetTotalAdvance(mLayoutPtr);
+ }
+
+ /**
+ * Effective ascent value of this positioned glyphs.
+ *
+ * If two or more font files are used in this series of glyphs, the effective ascent will be
+ * the minimum ascent value across the all font files.
+ *
+ * @return effective ascent value
+ */
+ public float getAscent() {
+ return nGetAscent(mLayoutPtr);
+ }
+
+ /**
+ * Effective descent value of this positioned glyphs.
+ *
+ * If two or more font files are used in this series of glyphs, the effective descent will be
+ * the maximum descent value across the all font files.
+ *
+ * @return effective descent value
+ */
+ public float getDescent() {
+ return nGetDescent(mLayoutPtr);
+ }
+
+ /**
+ * Returns the glyph style used for drawing the glyph at the given index.
+ *
+ * @return A glyph style
+ */
+ @NonNull
+ public GlyphStyle getStyle() {
+ return mGlyphStyle;
+ }
+
+ /**
+ * Returns the amount of X offset added to glyph position.
+ *
+ * @return The X offset added to glyph position.
+ */
+ public float getOriginX() {
+ return mXOffset;
+ }
+
+ /**
+ * Returns the amount of Y offset added to glyph position.
+ *
+ * @return The Y offset added to glyph position.
+ */
+ public float getOriginY() {
+ return mYOffset;
+ }
+
+ /**
+ * Returns the number of glyphs stored.
+ *
+ * @return the number of glyphs
+ */
+ @IntRange(from = 0)
+ public int glyphCount() {
+ return nGetGlyphCount(mLayoutPtr);
+ }
+
+ /**
+ * Returns the font object used for drawing the glyph at the given index.
+ *
+ * @param index the glyph index
+ * @return the font object used for drawing the glyph at the given index
+ */
+ @NonNull
+ public Font getFont(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return mFonts.get(index);
+ }
+
+ /**
+ * Returns the glyph ID used for drawing the glyph at the given index.
+ *
+ * @param index the glyph index
+ * @return A font object
+ */
+ @IntRange(from = 0)
+ public int getGlyphId(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return nGetGlyphId(mLayoutPtr, index);
+ }
+
+ /**
+ * Returns the x coordinate of the glyph position at the given index.
+ *
+ * @param index the glyph index
+ * @return A X offset in pixels
+ */
+ public float getPositionX(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return nGetX(mLayoutPtr, index) + mXOffset;
+ }
+
+ /**
+ * Returns the y coordinate of the glyph position at the given index.
+ *
+ * @param index the glyph index
+ * @return A Y offset in pixels.
+ */
+ public float getPositionY(@IntRange(from = 0) int index) {
+ Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
+ return nGetY(mLayoutPtr, index) + mYOffset;
+ }
+
+ /**
+ * Create single style layout from native result.
+ *
+ * @hide
+ *
+ * @param layoutPtr the address of native layout object.
+ * @param paint a paint object
+ */
+ public PositionedGlyphs(long layoutPtr, @NonNull Paint paint, float xOffset, float yOffset) {
+ mLayoutPtr = layoutPtr;
+ mGlyphStyle = new GlyphStyle(paint);
+ int glyphCount = nGetGlyphCount(layoutPtr);
+ mFonts = new ArrayList<>(glyphCount);
+ mXOffset = xOffset;
+ mYOffset = yOffset;
+
+ long prevPtr = 0;
+ Font prevFont = null;
+ for (int i = 0; i < glyphCount; ++i) {
+ long ptr = nGetFont(layoutPtr, i);
+ if (prevPtr != ptr) {
+ prevPtr = ptr;
+ prevFont = Font.findOrCreateFontFromNativePtr(ptr);
+ }
+ mFonts.add(prevFont);
+ }
+
+ REGISTRY.registerNativeAllocation(this, layoutPtr);
+ }
+
+ @CriticalNative
+ private static native int nGetGlyphCount(long minikinLayout);
+ @CriticalNative
+ private static native float nGetTotalAdvance(long minikinLayout);
+ @CriticalNative
+ private static native float nGetAscent(long minikinLayout);
+ @CriticalNative
+ private static native float nGetDescent(long minikinLayout);
+ @CriticalNative
+ private static native int nGetGlyphId(long minikinLayout, int i);
+ @CriticalNative
+ private static native float nGetX(long minikinLayout, int i);
+ @CriticalNative
+ private static native float nGetY(long minikinLayout, int i);
+ @CriticalNative
+ private static native long nGetFont(long minikinLayout, int i);
+ @CriticalNative
+ private static native long nReleaseFunc();
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof PositionedGlyphs)) return false;
+ PositionedGlyphs that = (PositionedGlyphs) o;
+
+ if (!mGlyphStyle.equals(that.mGlyphStyle)) return false;
+ if (mXOffset != that.mXOffset || mYOffset != that.mYOffset) return false;
+ if (glyphCount() != that.glyphCount()) return false;
+
+ for (int i = 0; i < glyphCount(); ++i) {
+ if (getGlyphId(i) != that.getGlyphId(i)) return false;
+ if (getPositionX(i) != that.getPositionX(i)) return false;
+ if (getPositionY(i) != that.getPositionY(i)) return false;
+ // Intentionally using reference equality since font equality is heavy due to buffer
+ // compare.
+ if (getFont(i) != that.getFont(i)) return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = Objects.hash(mXOffset, mYOffset, mGlyphStyle);
+ for (int i = 0; i < glyphCount(); ++i) {
+ hashCode = Objects.hash(hashCode,
+ getGlyphId(i), getPositionX(i), getPositionY(i), getFont(i));
+ }
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("[");
+ for (int i = 0; i < glyphCount(); ++i) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append("[ ID = " + getGlyphId(i) + ","
+ + " pos = (" + getPositionX(i) + "," + getPositionY(i) + ")"
+ + " font = " + getFont(i) + " ]");
+ }
+ sb.append("]");
+ return "PositionedGlyphs{"
+ + "glyphs = " + sb.toString()
+ + ", mXOffset=" + mXOffset
+ + ", mYOffset=" + mYOffset
+ + ", mGlyphStyle=" + mGlyphStyle
+ + '}';
+ }
+}
diff --git a/graphics/java/android/graphics/text/TextShaper.java b/graphics/java/android/graphics/text/TextShaper.java
new file mode 100644
index 0000000..f40ed8f
--- /dev/null
+++ b/graphics/java/android/graphics/text/TextShaper.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.text;
+
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.text.TextDirectionHeuristic;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.FastNative;
+
+/**
+ * Provides conversion from a text into glyph array.
+ *
+ * Text shaping is a preprocess for drawing text into canvas with glyphs. The glyph is a most
+ * primitive unit of the text drawing, consist of glyph identifier in the font file and its position
+ * and style. You can draw the shape result to Canvas by calling Canvas#drawGlyphs.
+
+ *
+ * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
+ * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
+ * @see android.text.StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic,
+ * TextPaint)
+ */
+public class TextShaper {
+ private TextShaper() {} // Do not instantiate
+
+ /**
+ * Shape non-styled text.
+ *
+ * This function shapes the text of the given range under the context of given context range.
+ * Some script, e.g. Arabic or Devanagari, changes letter shape based on its location or
+ * surrounding characters.
+ *
+ * @param text a text buffer to be shaped
+ * @param start a start index of shaping target in the buffer.
+ * @param count a length of shaping target in the buffer.
+ * @param contextStart a start index of context used for shaping in the buffer.
+ * @param contextCount a length of context used for shaping in the buffer.
+ * @param xOffset an additional amount of x offset of the result glyphs.
+ * @param yOffset an additional amount of y offset of the result glyphs.
+ * @param isRtl true if this text is shaped for RTL direction, false otherwise.
+ * @param paint a paint used for shaping text.
+ * @return a shape result.
+ */
+ @NonNull
+ public static PositionedGlyphs shapeTextRun(
+ @NonNull char[] text, int start, int count, int contextStart, int contextCount,
+ float xOffset, float yOffset, boolean isRtl, @NonNull Paint paint) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+ return new PositionedGlyphs(
+ nativeShapeTextRun(text, start, count, contextStart, contextCount, isRtl,
+ paint.getNativeInstance()),
+ paint, xOffset, yOffset);
+ }
+
+ /**
+ * Shape non-styled text.
+ *
+ * This function shapes the text of the given range under the context of given context range.
+ * Some script, e.g. Arabic or Devanagari, changes letter shape based on its location or
+ * surrounding characters.
+ *
+ * @param text a text buffer to be shaped. Any styled spans stored in this text are ignored.
+ * @param start a start index of shaping target in the buffer.
+ * @param count a length of shaping target in the buffer.
+ * @param contextStart a start index of context used for shaping in the buffer.
+ * @param contextCount a length of context used for shaping in the buffer.
+ * @param xOffset an additional amount of x offset of the result glyphs.
+ * @param yOffset an additional amount of y offset of the result glyphs.
+ * @param isRtl true if this text is shaped for RTL direction, false otherwise.
+ * @param paint a paint used for shaping text.
+ * @return a shape result
+ */
+ @NonNull
+ public static PositionedGlyphs shapeTextRun(
+ @NonNull CharSequence text, int start, int count, int contextStart, int contextCount,
+ float xOffset, float yOffset, boolean isRtl, @NonNull Paint paint) {
+ Preconditions.checkNotNull(text);
+ Preconditions.checkNotNull(paint);
+ if (text instanceof String) {
+ return new PositionedGlyphs(
+ nativeShapeTextRun(
+ (String) text, start, count, contextStart, contextCount, isRtl,
+ paint.getNativeInstance()),
+ paint, xOffset, yOffset);
+ } else {
+ char[] buf = new char[contextCount];
+ TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
+ return new PositionedGlyphs(
+ nativeShapeTextRun(
+ buf, start - contextStart, count,
+ 0, contextCount, isRtl, paint.getNativeInstance()),
+ paint, xOffset, yOffset);
+ }
+ }
+
+ @FastNative
+ private static native long nativeShapeTextRun(
+ char[] text, int start, int count, int contextStart, int contextCount,
+ boolean isRtl, long nativePaint);
+
+ @FastNative
+ private static native long nativeShapeTextRun(
+ String text, int start, int count, int contextStart, int contextCount,
+ boolean isRtl, long nativePaint);
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index c18e9ce..d810fb8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -43,6 +43,7 @@
import com.android.internal.view.IInputMethodManager;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
/**
* Manages IME control at the display-level. This occurs when IME comes up in multi-window mode.
@@ -62,15 +63,21 @@
private static final int FLOATING_IME_BOTTOM_INSET = -80;
protected final IWindowManager mWmService;
- protected final Handler mHandler;
+ protected final Executor mExecutor;
private final TransactionPool mTransactionPool;
private final DisplayController mDisplayController;
private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
+ @Deprecated
public DisplayImeController(IWindowManager wmService, DisplayController displayController,
Handler mainHandler, TransactionPool transactionPool) {
- mHandler = mainHandler;
+ this(wmService, displayController, mainHandler::post, transactionPool);
+ }
+
+ public DisplayImeController(IWindowManager wmService, DisplayController displayController,
+ Executor mainExecutor, TransactionPool transactionPool) {
+ mExecutor = mainExecutor;
mWmService = wmService;
mTransactionPool = transactionPool;
mDisplayController = displayController;
@@ -197,7 +204,7 @@
@Override
public void insetsChanged(InsetsState insetsState) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
if (mInsetsState.equals(insetsState)) {
return;
}
@@ -224,15 +231,25 @@
continue;
}
if (activeControl.getType() == InsetsState.ITYPE_IME) {
- mHandler.post(() -> {
+ mExecutor.execute(() -> {
final Point lastSurfacePosition = mImeSourceControl != null
? mImeSourceControl.getSurfacePosition() : null;
+ final boolean positionChanged =
+ !activeControl.getSurfacePosition().equals(lastSurfacePosition);
+ final boolean leashChanged =
+ !haveSameLeash(mImeSourceControl, activeControl);
mImeSourceControl = activeControl;
- if (!activeControl.getSurfacePosition().equals(lastSurfacePosition)
- && mAnimation != null) {
- startAnimation(mImeShowing, true /* forceRestart */);
- } else if (!mImeShowing) {
- removeImeSurface();
+ if (mAnimation != null) {
+ if (positionChanged) {
+ startAnimation(mImeShowing, true /* forceRestart */);
+ }
+ } else {
+ if (leashChanged) {
+ applyVisibilityToLeash();
+ }
+ if (!mImeShowing) {
+ removeImeSurface();
+ }
}
});
}
@@ -240,13 +257,27 @@
}
}
+ private void applyVisibilityToLeash() {
+ SurfaceControl leash = mImeSourceControl.getLeash();
+ if (leash != null) {
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (mImeShowing) {
+ t.show(leash);
+ } else {
+ t.hide(leash);
+ }
+ t.apply();
+ mTransactionPool.release(t);
+ }
+ }
+
@Override
public void showInsets(int types, boolean fromIme) {
if ((types & WindowInsets.Type.ime()) == 0) {
return;
}
if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
- mHandler.post(() -> startAnimation(true /* show */, false /* forceRestart */));
+ mExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */));
}
@Override
@@ -255,7 +286,7 @@
return;
}
if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
- mHandler.post(() -> startAnimation(false /* show */, false /* forceRestart */));
+ mExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */));
}
@Override
@@ -495,4 +526,20 @@
return IInputMethodManager.Stub.asInterface(
ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
}
+
+ private static boolean haveSameLeash(InsetsSourceControl a, InsetsSourceControl b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null || b == null) {
+ return false;
+ }
+ if (a.getLeash() == b.getLeash()) {
+ return true;
+ }
+ if (a.getLeash() == null || b.getLeash() == null) {
+ return false;
+ }
+ return a.getLeash().isSameSurface(b.getLeash());
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index b4620e27..84b98f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -271,14 +271,14 @@
}
@Override
- public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, ClientWindowFrames outFrames,
MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Point outSurfaceSize,
SurfaceControl outBLASTSurfaceControl) {
- int res = super.relayout(window, seq, attrs, requestedWidth, requestedHeight,
+ int res = super.relayout(window, attrs, requestedWidth, requestedHeight,
viewVisibility, flags, frameNumber, outFrames,
mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
@@ -365,10 +365,6 @@
public void updatePointerIcon(float x, float y) {}
@Override
- public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility,
- int localValue, int localChanges) {}
-
- @Override
public void dispatchWindowShown() {}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 9954618..6bc838f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -239,9 +239,14 @@
throw new RuntimeException("Callers should call scheduleOffset() instead of this "
+ "directly");
}
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
mDisplayAreaMap.forEach(
- (key, leash) -> animateWindows(leash, fromBounds, toBounds, direction,
- durationMs));
+ (key, leash) -> {
+ animateWindows(leash, fromBounds, toBounds, direction,
+ durationMs);
+ wct.setBounds(key.token, toBounds);
+ });
+ applyTransaction(wct);
}
private void resetWindowsOffset() {
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 7c0c738..b6b518d 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
@@ -184,9 +184,8 @@
mDisplaySize.x, mTutorialAreaHeight, 0, 0,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
lp.gravity = Gravity.TOP | Gravity.LEFT;
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setFitInsetsTypes(0 /* types */);
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 edbbd69..cdb27da 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,34 +1319,6 @@
mBackground.getRight(), mBackground.getBottom(), Op.UNION);
}
- void onDockedTopTask() {
- mState.animateAfterRecentsDrawn = true;
- startDragging(false /* animate */, false /* touching */);
- updateDockSide();
- mEntranceAnimationRunning = true;
-
- resizeStackSurfaces(calculatePositionForInsetBounds(),
- mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
- mSplitLayout.getSnapAlgorithm().getMiddleTarget(),
- null /* transaction */);
- }
-
- void onRecentsDrawn() {
- updateDockSide();
- final int position = calculatePositionForInsetBounds();
- if (mState.animateAfterRecentsDrawn) {
- mState.animateAfterRecentsDrawn = false;
-
- mHandler.post(() -> {
- // Delay switching resizing mode because this might cause jank in recents animation
- // that's longer than this animation.
- stopDragging(position, getSnapAlgorithm().getMiddleTarget(),
- mLongPressEntraceAnimDuration, Interpolators.FAST_OUT_SLOW_IN,
- 200 /* endDelay */);
- });
- }
- }
-
void onUndockingTask() {
int dockSide = mSplitLayout.getPrimarySplitSide();
if (inSplitMode()) {
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 58106c9..985dff2 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
@@ -48,14 +48,6 @@
/** Switch to minimized state if appropriate. */
void setMinimized(boolean minimized);
- /**
- * Workaround for b/62528361, at the time recents has drawn, it may happen before a
- * configuration change to the Divider, and internally, the event will be posted to the
- * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
- * register the event handler here and proxy the event to the current DividerView.
- */
- void onRecentsDrawn();
-
/** Called when there's an activity forced resizable. */
void onActivityForcedResizable(String packageName, int taskId, int reason);
@@ -68,9 +60,6 @@
/** Called when there's a task undocking. */
void onUndockingTask();
- /** Called when top task docked. */
- void onDockedTopTask();
-
/** Called when app transition finished. */
void onAppTransitionFinished();
@@ -85,4 +74,13 @@
/** @return the container token for the secondary split root task. */
WindowContainerToken getSecondaryRoot();
+
+ /**
+ * Splits the primary task if feasible, this is to preserve legacy way to toggle split screen.
+ * Like triggering split screen through long pressing recents app button or through
+ * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN}.
+ *
+ * @return {@code true} if it successes to split the primary task.
+ */
+ boolean splitPrimaryTask();
}
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 ce49dd9..43e4d62 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
@@ -16,19 +16,25 @@
package com.android.wm.shell.splitscreen;
+import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
+import android.os.RemoteException;
import android.provider.Settings;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.Toast;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -47,6 +53,7 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -57,7 +64,7 @@
DisplayController.OnDisplaysChangedListener {
static final boolean DEBUG = false;
- private static final String TAG = "Divider";
+ private static final String TAG = "SplitScreenCtrl";
private static final int DEFAULT_APP_TRANSITION_DURATION = 336;
private final Context mContext;
@@ -157,12 +164,12 @@
// Don't initialize the divider or anything until we get the default display.
}
- /** Returns {@code true} if split screen is supported on the device. */
+ @Override
public boolean isSplitScreenSupported() {
return mSplits.isSplitScreenSupported();
}
- /** Called when keyguard showing state changed. */
+ @Override
public void onKeyguardVisibilityChanged(boolean showing) {
if (!isSplitActive() || mView == null) {
return;
@@ -229,21 +236,22 @@
mHandler.post(task);
}
- /** Returns {@link DividerView}. */
+ @Override
public DividerView getDividerView() {
return mView;
}
- /** Returns {@code true} if one of the split screen is in minimized mode. */
+ @Override
public boolean isMinimized() {
return mMinimized;
}
+ @Override
public boolean isHomeStackResizable() {
return mHomeStackResizable;
}
- /** Returns {@code true} if the divider is visible. */
+ @Override
public boolean isDividerVisible() {
return mView != null && mView.getVisibility() == View.VISIBLE;
}
@@ -328,7 +336,7 @@
}
}
- /** Switch to minimized state if appropriate. */
+ @Override
public void setMinimized(final boolean minimized) {
if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
mHandler.post(() -> {
@@ -392,48 +400,29 @@
mWindowManager.setTouchable(!mAdjustedForIme);
}
- /**
- * Workaround for b/62528361, at the time recents has drawn, it may happen before a
- * configuration change to the Divider, and internally, the event will be posted to the
- * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
- * register the event handler here and proxy the event to the current DividerView.
- */
- public void onRecentsDrawn() {
- if (mView != null) {
- mView.onRecentsDrawn();
- }
- }
-
- /** Called when there's an activity forced resizable. */
+ @Override
public void onActivityForcedResizable(String packageName, int taskId, int reason) {
mForcedResizableController.activityForcedResizable(packageName, taskId, reason);
}
- /** Called when there's an activity dismissing split screen. */
+ @Override
public void onActivityDismissingSplitScreen() {
mForcedResizableController.activityDismissingSplitScreen();
}
- /** Called when there's an activity launch on secondary display failed. */
+ @Override
public void onActivityLaunchOnSecondaryDisplayFailed() {
mForcedResizableController.activityLaunchOnSecondaryDisplayFailed();
}
- /** Called when there's a task undocking. */
+ @Override
public void onUndockingTask() {
if (mView != null) {
mView.onUndockingTask();
}
}
- /** Called when top task docked. */
- public void onDockedTopTask() {
- if (mView != null) {
- mView.onDockedTopTask();
- }
- }
-
- /** Called when app transition finished. */
+ @Override
public void onAppTransitionFinished() {
if (mView == null) {
return;
@@ -441,7 +430,7 @@
mForcedResizableController.onAppTransitionFinished();
}
- /** Dumps current status of Split Screen. */
+ @Override
public void dump(PrintWriter pw) {
pw.print(" mVisible="); pw.println(mVisible);
pw.print(" mMinimized="); pw.println(mMinimized);
@@ -458,7 +447,7 @@
return (long) (transitionDuration * transitionScale);
}
- /** Registers listener that gets called whenever the existence of the divider changes. */
+ @Override
public void registerInSplitScreenListener(Consumer<Boolean> listener) {
listener.accept(isDividerVisible());
synchronized (mDockedStackExistsListeners) {
@@ -473,6 +462,42 @@
}
}
+ @Override
+ public boolean splitPrimaryTask() {
+ try {
+ if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED
+ || isSplitActive()) {
+ return false;
+ }
+
+ // Try fetching the top running task.
+ final List<RunningTaskInfo> runningTasks =
+ ActivityTaskManager.getService().getTasks(1 /* maxNum */);
+ if (runningTasks == null || runningTasks.isEmpty()) {
+ return false;
+ }
+ // Note: The set of running tasks from the system is ordered by recency.
+ final RunningTaskInfo topRunningTask = runningTasks.get(0);
+
+ final int activityType = topRunningTask.configuration.windowConfiguration
+ .getActivityType();
+ if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
+ return false;
+ }
+
+ if (!topRunningTask.supportsSplitScreenMultiWindow) {
+ Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
+ Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(
+ topRunningTask.taskId, true /* onTop */);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
/** Notifies the bounds of split screen changed. */
void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
synchronized (mBoundsChangedListeners) {
@@ -532,7 +557,7 @@
return mWindowManagerProxy;
}
- /** @return the container token for the secondary split root task. */
+ @Override
public WindowContainerToken getSecondaryRoot() {
if (mSplits == null || mSplits.mSecondary == null) {
return null;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
new file mode 100644
index 0000000..080cddc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InsetsState.ITYPE_IME;
+import static android.view.Surface.ROTATION_0;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.graphics.Point;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.view.IInputMethodManager;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class DisplayImeControllerTest {
+
+ private SurfaceControl.Transaction mT;
+ private DisplayImeController.PerDisplay mPerDisplay;
+ private IInputMethodManager mMock;
+
+ @Before
+ public void setUp() throws Exception {
+ mT = mock(SurfaceControl.Transaction.class);
+ mMock = mock(IInputMethodManager.class);
+ mPerDisplay = new DisplayImeController(null, null, Runnable::run, new TransactionPool() {
+ @Override
+ public SurfaceControl.Transaction acquire() {
+ return mT;
+ }
+
+ @Override
+ public void release(SurfaceControl.Transaction t) {
+ }
+ }) {
+ @Override
+ public IInputMethodManager getImms() {
+ return mMock;
+ }
+ }.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0);
+ }
+
+ @Test
+ public void reappliesVisibilityToChangedLeash() {
+ verifyZeroInteractions(mT);
+
+ mPerDisplay.mImeShowing = false;
+ mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] {
+ new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0))
+ });
+
+ verify(mT).hide(any());
+
+ mPerDisplay.mImeShowing = true;
+ mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[] {
+ new InsetsSourceControl(ITYPE_IME, mock(SurfaceControl.class), new Point(0, 0))
+ });
+
+ verify(mT).show(any());
+ }
+}
diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
index 73c76f0..0bee609 100644
--- a/libs/androidfw/include/androidfw/CursorWindow.h
+++ b/libs/androidfw/include/androidfw/CursorWindow.h
@@ -170,7 +170,7 @@
Header* mHeader;
inline void* offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) {
- if (offset >= mSize) {
+ if (offset > mSize) {
ALOGE("Offset %" PRIu32 " out of bounds, max value %zu", offset, mSize);
return NULL;
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 90d2537..975265e 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -327,6 +327,7 @@
"jni/PathMeasure.cpp",
"jni/Picture.cpp",
"jni/Shader.cpp",
+ "jni/RenderEffect.cpp",
"jni/Typeface.cpp",
"jni/Utils.cpp",
"jni/YuvToJpegEncoder.cpp",
@@ -334,6 +335,7 @@
"jni/fonts/FontFamily.cpp",
"jni/text/LineBreaker.cpp",
"jni/text/MeasuredText.cpp",
+ "jni/text/TextShaper.cpp",
],
header_libs: [ "android_graphics_jni_headers" ],
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ff9cf45..8fba9cf 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -49,6 +49,12 @@
return true;
}
+bool LayerProperties::setImageFilter(SkImageFilter* imageFilter) {
+ if(mImageFilter.get() == imageFilter) return false;
+ mImageFilter = sk_ref_sp(imageFilter);
+ return true;
+}
+
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
@@ -63,6 +69,7 @@
setAlpha(other.alpha());
setXferMode(other.xferMode());
setColorFilter(other.getColorFilter());
+ setImageFilter(other.getImageFilter());
return *this;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index ef4cd1f..aeb60e6 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -27,6 +27,7 @@
#include "utils/PaintUtils.h"
#include <SkBlendMode.h>
+#include <SkImageFilter.h>
#include <SkCamera.h>
#include <SkColor.h>
#include <SkMatrix.h>
@@ -93,6 +94,10 @@
SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
+ bool setImageFilter(SkImageFilter* imageFilter);
+
+ SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
+
// Sets alpha, xfermode, and colorfilter from an SkPaint
// paint may be NULL, in which case defaults will be set
bool setFromPaint(const SkPaint* paint);
@@ -118,6 +123,7 @@
uint8_t mAlpha;
SkBlendMode mMode;
sk_sp<SkColorFilter> mColorFilter;
+ sk_sp<SkImageFilter> mImageFilter;
};
/*
@@ -541,6 +547,7 @@
bool promotedToLayer() const {
return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
(mComputedFields.mNeedLayerForFunctors ||
+ mLayerProperties.mImageFilter != nullptr ||
(!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
mPrimitiveFields.mHasOverlappingRendering));
}
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index 4bbf121..dca10e2 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -47,6 +47,7 @@
extern int register_android_graphics_NinePatch(JNIEnv*);
extern int register_android_graphics_PathEffect(JNIEnv* env);
extern int register_android_graphics_Shader(JNIEnv* env);
+extern int register_android_graphics_RenderEffect(JNIEnv* env);
extern int register_android_graphics_Typeface(JNIEnv* env);
namespace android {
@@ -108,6 +109,7 @@
{"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
{"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
+ {"android.graphics.RenderEffect", REG_JNI(register_android_graphics_RenderEffect)},
{"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
{"android.graphics.animation.NativeInterpolatorFactory",
REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)},
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 12e2e81..e1f5abd7 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -43,6 +43,7 @@
extern int register_android_graphics_NinePatch(JNIEnv*);
extern int register_android_graphics_PathEffect(JNIEnv* env);
extern int register_android_graphics_Shader(JNIEnv* env);
+extern int register_android_graphics_RenderEffect(JNIEnv* env);
extern int register_android_graphics_Typeface(JNIEnv* env);
extern int register_android_graphics_YuvImage(JNIEnv* env);
@@ -73,6 +74,7 @@
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
+extern int register_android_graphics_text_TextShaper(JNIEnv *env);
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
@@ -123,6 +125,7 @@
REG_JNI(register_android_graphics_Picture),
REG_JNI(register_android_graphics_Region),
REG_JNI(register_android_graphics_Shader),
+ REG_JNI(register_android_graphics_RenderEffect),
REG_JNI(register_android_graphics_TextureLayer),
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_YuvImage),
@@ -137,6 +140,7 @@
REG_JNI(register_android_graphics_pdf_PdfRenderer),
REG_JNI(register_android_graphics_text_MeasuredText),
REG_JNI(register_android_graphics_text_LineBreaker),
+ REG_JNI(register_android_graphics_text_TextShaper),
REG_JNI(register_android_util_PathParser),
REG_JNI(register_android_view_RenderNode),
diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h
index 6cde9c5..a15803a 100644
--- a/libs/hwui/hwui/MinikinUtils.h
+++ b/libs/hwui/hwui/MinikinUtils.h
@@ -72,7 +72,7 @@
size_t start = 0;
size_t nGlyphs = layout.nGlyphs();
for (size_t i = 0; i < nGlyphs; i++) {
- const minikin::MinikinFont* nextFont = layout.getFont(i);
+ const minikin::MinikinFont* nextFont = layout.getFont(i)->typeface().get();
if (i > 0 && nextFont != curFont) {
SkFont* skfont = &paint->getSkFont();
MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start));
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 0bb689c..4e2016a 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -157,7 +157,6 @@
private:
using SkPaint::setShader;
- using SkPaint::setImageFilter;
SkFont mFont;
sk_sp<SkDrawLooper> mLooper;
diff --git a/libs/hwui/jni/FontUtils.h b/libs/hwui/jni/FontUtils.h
index b36b4e6..f93a0da 100644
--- a/libs/hwui/jni/FontUtils.h
+++ b/libs/hwui/jni/FontUtils.h
@@ -38,6 +38,14 @@
minikin::Font font;
};
+// We assume FontWrapper's address is the same as underlying Font's address.
+// This assumption is used for looking up Java font object from native address.
+// The Font object can be created without Java's Font object but all Java's Font objects point to
+// the native FontWrapper. So when looking up Java object from minikin::Layout which gives us Font
+// address, we lookup Font Java object from Font address with assumption that it is the same as
+// FontWrapper address.
+static_assert(offsetof(FontWrapper, font) == 0);
+
// Utility wrapper for java.util.List
class ListHelper {
public:
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
new file mode 100644
index 0000000..0ebd0ca
--- /dev/null
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "Bitmap.h"
+#include "GraphicsJNI.h"
+#include "SkImageFilter.h"
+#include "SkImageFilters.h"
+#include "graphics_jni_helpers.h"
+#include "utils/Blur.h"
+#include <utils/Log.h>
+
+using namespace android::uirenderer;
+
+static jlong createOffsetEffect(
+ JNIEnv* env,
+ jobject,
+ jfloat offsetX,
+ jfloat offsetY,
+ jlong inputFilterHandle
+) {
+ auto* inputFilter = reinterpret_cast<const SkImageFilter*>(inputFilterHandle);
+ sk_sp<SkImageFilter> offset = SkImageFilters::Offset(offsetX, offsetY, sk_ref_sp(inputFilter));
+ return reinterpret_cast<jlong>(offset.release());
+}
+
+static jlong createBlurEffect(JNIEnv* env , jobject, jfloat radiusX,
+ jfloat radiusY, jlong inputFilterHandle, jint edgeTreatment) {
+ auto* inputImageFilter = reinterpret_cast<SkImageFilter*>(inputFilterHandle);
+ sk_sp<SkImageFilter> blurFilter =
+ SkImageFilters::Blur(
+ Blur::convertRadiusToSigma(radiusX),
+ Blur::convertRadiusToSigma(radiusY),
+ static_cast<SkTileMode>(edgeTreatment),
+ sk_ref_sp(inputImageFilter),
+ nullptr);
+ return reinterpret_cast<jlong>(blurFilter.release());
+}
+
+static void RenderEffect_safeUnref(SkImageFilter* filter) {
+ SkSafeUnref(filter);
+}
+
+static jlong getRenderEffectFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&RenderEffect_safeUnref));
+}
+
+static const JNINativeMethod gRenderEffectMethods[] = {
+ {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
+ {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
+ {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect}
+};
+
+int register_android_graphics_RenderEffect(JNIEnv* env) {
+ android::RegisterMethodsOrDie(env, "android/graphics/RenderEffect",
+ gRenderEffectMethods, NELEM(gRenderEffectMethods));
+ return 0;
+}
\ No newline at end of file
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 85c802b..4b4aa92 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -215,6 +215,12 @@
return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA);
}
+static jboolean android_view_RenderNode_setRenderEffect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+ jlong renderEffectPtr) {
+ SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr);
+ return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC);
+}
+
static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
bool hasOverlappingRendering) {
return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
@@ -690,6 +696,7 @@
{ "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
{ "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha },
+ { "nSetRenderEffect", "(JJ)V", (void*) android_view_RenderNode_setRenderEffect },
{ "nSetHasOverlappingRendering", "(JZ)Z",
(void*) android_view_RenderNode_setHasOverlappingRendering },
{ "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint },
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index bcdb4f5..0eb4095 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -198,6 +198,59 @@
return spacing;
}
+// Critical Native
+static jlong Font_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+
+ uint64_t result = font->font.style().weight();
+ result |= font->font.style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
+ result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
+ result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
+ return result;
+}
+
+// Critical Native
+static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
+ const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
+ uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
+ return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct FontBufferWrapper {
+ FontBufferWrapper(const std::shared_ptr<minikin::MinikinFont>& font) : minikinFont(font) {}
+ // MinikinFont holds a shared pointer of SkTypeface which has reference to font data.
+ std::shared_ptr<minikin::MinikinFont> minikinFont;
+};
+
+static void unrefBuffer(jlong nativePtr) {
+ FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
+ delete wrapper;
+}
+
+// Critical Native
+static jlong FontBufferHelper_refFontBuffer(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
+ FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
+ return reinterpret_cast<jlong>(new FontBufferWrapper(font->font.typeface()));
+}
+
+// Fast Native
+static jobject FontBufferHelper_wrapByteBuffer(JNIEnv* env, jobject, jlong nativePtr) {
+ FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
+ return env->NewDirectByteBuffer(
+ const_cast<void*>(wrapper->minikinFont->GetFontData()),
+ wrapper->minikinFont->GetFontSize());
+}
+
+// Critical Native
+static jlong FontBufferHelper_getReleaseFunc(CRITICAL_JNI_PARAMS) {
+ return reinterpret_cast<jlong>(unrefBuffer);
+}
+
///////////////////////////////////////////////////////////////////////////////
static const JNINativeMethod gFontBuilderMethods[] = {
@@ -211,13 +264,23 @@
static const JNINativeMethod gFontMethods[] = {
{ "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
{ "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
+ { "nGetFontInfo", "(J)J", (void*) Font_getFontInfo },
+ { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo },
+};
+
+static const JNINativeMethod gFontBufferHelperMethods[] = {
+ { "nRefFontBuffer", "(J)J", (void*) FontBufferHelper_refFontBuffer },
+ { "nWrapByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) FontBufferHelper_wrapByteBuffer },
+ { "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc },
};
int register_android_graphics_fonts_Font(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
NELEM(gFontBuilderMethods)) +
RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
- NELEM(gFontMethods));
+ NELEM(gFontMethods)) +
+ RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper",
+ gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods));
}
}
diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp
new file mode 100644
index 0000000..9d9e91f
--- /dev/null
+++ b/libs/hwui/jni/text/TextShaper.cpp
@@ -0,0 +1,206 @@
+/*
+ * 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "TextShaper"
+
+#include "graphics_jni_helpers.h"
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <set>
+#include <algorithm>
+
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <minikin/MinikinPaint.h>
+#include <minikin/MinikinFont.h>
+
+namespace android {
+
+struct LayoutWrapper {
+ LayoutWrapper(minikin::Layout&& layout, float ascent, float descent)
+ : layout(std::move(layout)), ascent(ascent), descent(descent) {}
+ minikin::Layout layout;
+ float ascent;
+ float descent;
+};
+
+static void releaseLayout(jlong ptr) {
+ delete reinterpret_cast<LayoutWrapper*>(ptr);
+}
+
+static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int count,
+ int contextStart, int contextCount, minikin::Bidi bidiFlags,
+ const Paint& paint, const Typeface* typeface) {
+
+ minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(&paint, typeface);
+
+ minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface,
+ text, textSize, start, count, contextStart, contextCount, nullptr);
+
+ std::set<const minikin::Font*> seenFonts;
+ float overallAscent = 0;
+ float overallDescent = 0;
+ for (int i = 0; i < layout.nGlyphs(); ++i) {
+ const minikin::Font* font = layout.getFont(i);
+ if (seenFonts.find(font) != seenFonts.end()) continue;
+ minikin::MinikinExtent extent = {};
+ font->typeface()->GetFontExtent(&extent, minikinPaint, layout.getFakery(i));
+ overallAscent = std::min(overallAscent, extent.ascent);
+ overallDescent = std::max(overallDescent, extent.descent);
+ }
+
+ std::unique_ptr<LayoutWrapper> ptr = std::make_unique<LayoutWrapper>(
+ std::move(layout), overallAscent, overallDescent
+ );
+
+ return reinterpret_cast<jlong>(ptr.release());
+}
+
+static jlong TextShaper_shapeTextRunChars(JNIEnv *env, jobject, jcharArray charArray,
+ jint start, jint count, jint contextStart, jint contextCount, jboolean isRtl,
+ jlong paintPtr) {
+ ScopedCharArrayRO text(env, charArray);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ return shapeTextRun(
+ text.get(), text.size(),
+ start, count,
+ contextStart, contextCount,
+ bidiFlags,
+ *paint, typeface);
+
+}
+
+static jlong TextShaper_shapeTextRunString(JNIEnv *env, jobject, jstring string,
+ jint start, jint count, jint contextStart, jint contextCount, jboolean isRtl,
+ jlong paintPtr) {
+ ScopedStringChars text(env, string);
+ Paint* paint = reinterpret_cast<Paint*>(paintPtr);
+ const Typeface* typeface = paint->getAndroidTypeface();
+ const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
+ return shapeTextRun(
+ text.get(), text.size(),
+ start, count,
+ contextStart, contextCount,
+ bidiFlags,
+ *paint, typeface);
+}
+
+// CriticalNative
+static jint TextShaper_Result_getGlyphCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.nGlyphs();
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getTotalAdvance(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getAdvance();
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getAscent(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->ascent;
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getDescent(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->descent;
+}
+
+// CriticalNative
+static jint TextShaper_Result_getGlyphId(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getGlyphId(i);
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getX(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getX(i);
+}
+
+// CriticalNative
+static jfloat TextShaper_Result_getY(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return layout->layout.getY(i);
+}
+
+// CriticalNative
+static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
+ const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
+ return reinterpret_cast<jlong>(layout->layout.getFont(i));
+}
+
+// CriticalNative
+static jlong TextShaper_Result_nReleaseFunc(CRITICAL_JNI_PARAMS) {
+ return reinterpret_cast<jlong>(releaseLayout);
+}
+
+static const JNINativeMethod gMethods[] = {
+ // Fast Natives
+ {"nativeShapeTextRun", "("
+ "[C" // text
+ "I" // start
+ "I" // count
+ "I" // contextStart
+ "I" // contextCount
+ "Z" // isRtl
+ "J)" // paint
+ "J", // LayoutPtr
+ (void*) TextShaper_shapeTextRunChars},
+
+ {"nativeShapeTextRun", "("
+ "Ljava/lang/String;" // text
+ "I" // start
+ "I" // count
+ "I" // contextStart
+ "I" // contextCount
+ "Z" // isRtl
+ "J)" // paint
+ "J", // LayoutPtr
+ (void*) TextShaper_shapeTextRunString},
+
+};
+
+static const JNINativeMethod gResultMethods[] = {
+ { "nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount },
+ { "nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance },
+ { "nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent },
+ { "nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent },
+ { "nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId },
+ { "nGetX", "(JI)F", (void*)TextShaper_Result_getX },
+ { "nGetY", "(JI)F", (void*)TextShaper_Result_getY },
+ { "nGetFont", "(JI)J", (void*)TextShaper_Result_getFont },
+ { "nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc },
+};
+
+int register_android_graphics_text_TextShaper(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/text/TextShaper", gMethods,
+ NELEM(gMethods))
+ + RegisterMethodsOrDie(env, "android/graphics/text/PositionedGlyphs",
+ gResultMethods, NELEM(gResultMethods));
+}
+
+}
+
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 00ceb2d..1473b3e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -172,10 +172,12 @@
SkPaint* paint) {
paint->setFilterQuality(kLow_SkFilterQuality);
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
- properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr) {
+ properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
+ properties.getImageFilter() != nullptr) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
+ paint->setImageFilter(sk_ref_sp(properties.getImageFilter()));
return true;
}
return false;
diff --git a/media/java/android/media/MediaMetadata.java b/media/java/android/media/MediaMetadata.java
index 523a072..dbf4ad0 100644
--- a/media/java/android/media/MediaMetadata.java
+++ b/media/java/android/media/MediaMetadata.java
@@ -18,13 +18,13 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.StringDef;
-import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.browse.MediaBrowser;
import android.media.session.MediaController;
+import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -417,14 +417,17 @@
}
private final Bundle mBundle;
+ private final int mBitmapDimensionLimit;
private MediaDescription mDescription;
- private MediaMetadata(Bundle bundle) {
+ private MediaMetadata(Bundle bundle, int bitmapDimensionLimit) {
mBundle = new Bundle(bundle);
+ mBitmapDimensionLimit = bitmapDimensionLimit;
}
private MediaMetadata(Parcel in) {
mBundle = in.readBundle();
+ mBitmapDimensionLimit = Math.max(in.readInt(), 0);
}
/**
@@ -513,6 +516,22 @@
return bmp;
}
+ /**
+ * Gets the width/height limit (in pixels) for the bitmaps when this metadata was created.
+ * This method returns a positive value or zero.
+ * <p>
+ * If it returns a positive value, then all the bitmaps in this metadata has width/height
+ * not greater than this limit. Bitmaps may have been scaled down according to the limit.
+ * <p>
+ * If it returns zero, then no scaling down was applied to the bitmaps when this metadata
+ * was created.
+ *
+ * @see Builder#setBitmapDimensionLimit(int)
+ */
+ public @IntRange(from = 0) int getBitmapDimensionLimit() {
+ return mBitmapDimensionLimit;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -521,6 +540,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeBundle(mBundle);
+ dest.writeInt(mBitmapDimensionLimit);
}
/**
@@ -718,6 +738,7 @@
*/
public static final class Builder {
private final Bundle mBundle;
+ private int mBitmapDimensionLimit;
/**
* Create an empty Builder. Any field that should be included in the
@@ -736,30 +757,7 @@
*/
public Builder(MediaMetadata source) {
mBundle = new Bundle(source.mBundle);
- }
-
- /**
- * Create a Builder using a {@link MediaMetadata} instance to set
- * initial values, but replace bitmaps with a scaled down copy if their width (or height)
- * is larger than maxBitmapSize.
- *
- * @param source The original metadata to copy.
- * @param maxBitmapSize The maximum height/width for bitmaps contained
- * in the metadata.
- * @hide
- */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public Builder(@NonNull MediaMetadata source, @IntRange(from = 1) int maxBitmapSize) {
- this(source);
- for (String key : mBundle.keySet()) {
- Object value = mBundle.get(key);
- if (value != null && value instanceof Bitmap) {
- Bitmap bmp = (Bitmap) value;
- if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
- putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
- }
- }
- }
+ mBitmapDimensionLimit = source.mBitmapDimensionLimit;
}
/**
@@ -902,9 +900,9 @@
* <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
* </ul>
* <p>
- * Large bitmaps may be scaled down by the system when
- * {@link android.media.session.MediaSession#setMetadata} is called.
- * To pass full resolution images {@link Uri Uris} should be used with
+ * Large bitmaps may be scaled down by the system with
+ * {@link Builder#setBitmapDimensionLimit(int)} when {@link MediaSession#setMetadata}
+ * is called. To pass full resolution images {@link Uri Uris} should be used with
* {@link #putString}.
*
* @param key The key for referencing this value
@@ -923,18 +921,46 @@
}
/**
+ * Sets the maximum width/height (in pixels) for the bitmaps in the metadata.
+ * Bitmaps will be replaced with scaled down copies if their width (or height) is
+ * larger than {@code bitmapDimensionLimit}.
+ * <p>
+ * In order to unset the limit, pass zero as {@code bitmapDimensionLimit}.
+ *
+ * @param bitmapDimensionLimit The maximum width/height (in pixels) for bitmaps
+ * contained in the metadata. Pass {@code 0} to unset the limit.
+ */
+ @NonNull
+ public Builder setBitmapDimensionLimit(int bitmapDimensionLimit) {
+ mBitmapDimensionLimit = Math.max(bitmapDimensionLimit, 0);
+ return this;
+ }
+
+ /**
* Creates a {@link MediaMetadata} instance with the specified fields.
*
* @return The new MediaMetadata instance
*/
public MediaMetadata build() {
- return new MediaMetadata(mBundle);
+ if (mBitmapDimensionLimit > 0) {
+ for (String key : mBundle.keySet()) {
+ Object value = mBundle.get(key);
+ if (value instanceof Bitmap) {
+ Bitmap bmp = (Bitmap) value;
+ if (bmp.getHeight() > mBitmapDimensionLimit
+ || bmp.getWidth() > mBitmapDimensionLimit) {
+ putBitmap(key, scaleBitmap(bmp, mBitmapDimensionLimit));
+ }
+ }
+ }
+ }
+ return new MediaMetadata(mBundle, mBitmapDimensionLimit);
}
- private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
- float maxSizeF = maxSize;
- float widthScale = maxSizeF / bmp.getWidth();
- float heightScale = maxSizeF / bmp.getHeight();
+ private Bitmap scaleBitmap(Bitmap bmp, int maxDimension) {
+ float maxDimensionF = maxDimension;
+ float widthScale = maxDimensionF / bmp.getWidth();
+ float heightScale = maxDimensionF / bmp.getHeight();
float scale = Math.min(widthScale, heightScale);
int height = (int) (bmp.getHeight() * scale);
int width = (int) (bmp.getWidth() * scale);
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index d35bc41..d02b496 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -372,7 +372,7 @@
AudioAttributes.toLegacyStreamType(mAudioAttributes)) != 0) {
startLocalPlayer();
}
- } else if (mAllowRemote && (mRemotePlayer != null)) {
+ } else if (mAllowRemote && (mRemotePlayer != null) && (mUri != null)) {
final Uri canonicalUri = mUri.getCanonicalUri();
final boolean looping;
final float volume;
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 8deb0c4..9deeb8f 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -34,6 +34,7 @@
import android.content.pm.UserInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.database.StaleDataException;
import android.net.Uri;
import android.os.Environment;
import android.os.FileUtils;
@@ -492,7 +493,12 @@
public Uri getRingtoneUri(int position) {
// use cursor directly instead of requerying it, which could easily
// cause position to shuffle.
- if (mCursor == null || !mCursor.moveToPosition(position)) {
+ try {
+ if (mCursor == null || !mCursor.moveToPosition(position)) {
+ return null;
+ }
+ } catch (StaleDataException | IllegalStateException e) {
+ Log.e(TAG, "Unexpected Exception has been catched.", e);
return null;
}
@@ -1130,11 +1136,13 @@
// Try finding the scanned ringtone
final String filename = getDefaultRingtoneFilename(type);
+ final String whichAudio = getQueryStringForType(type);
+ final String where = MediaColumns.DISPLAY_NAME + "=? AND " + whichAudio + "=?";
final Uri baseUri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
try (Cursor cursor = context.getContentResolver().query(baseUri,
new String[] { MediaColumns._ID },
- MediaColumns.DISPLAY_NAME + "=?",
- new String[] { filename }, null)) {
+ where,
+ new String[] { filename, "1" }, null)) {
if (cursor.moveToFirst()) {
final Uri ringtoneUri = context.getContentResolver().canonicalizeOrElse(
ContentUris.withAppendedId(baseUri, cursor.getLong(0)));
@@ -1162,4 +1170,13 @@
default: throw new IllegalArgumentException();
}
}
+
+ private static String getQueryStringForType(int type) {
+ switch (type) {
+ case TYPE_RINGTONE: return MediaStore.Audio.AudioColumns.IS_RINGTONE;
+ case TYPE_NOTIFICATION: return MediaStore.Audio.AudioColumns.IS_NOTIFICATION;
+ case TYPE_ALARM: return MediaStore.Audio.AudioColumns.IS_ALARM;
+ default: throw new IllegalArgumentException();
+ }
+ }
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 624607b..e17e069 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -464,7 +464,9 @@
int fields = 0;
MediaDescription description = null;
if (metadata != null) {
- metadata = (new MediaMetadata.Builder(metadata, mMaxBitmapSize)).build();
+ metadata = new MediaMetadata.Builder(metadata)
+ .setBitmapDimensionLimit(mMaxBitmapSize)
+ .build();
if (metadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
}
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 1fbb672..4380c13 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -25,6 +25,7 @@
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.ITvInputManagerCallback;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
@@ -88,6 +89,8 @@
void timeShiftSetPlaybackParams(in IBinder sessionToken, in PlaybackParams params, int userId);
void timeShiftEnablePositionTracking(in IBinder sessionToken, boolean enable, int userId);
+ List<TvChannelInfo> getCurrentTvChannelInfos(int userId);
+
// For the recording session
void startRecording(in IBinder sessionToken, in Uri programUri, in Bundle params, int userId);
void stopRecording(in IBinder sessionToken, int userId);
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
index 0f8bf2f..9f80bf5 100644
--- a/media/java/android/media/tv/ITvInputManagerCallback.aidl
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvInputInfo;
/**
@@ -28,4 +29,5 @@
void onInputUpdated(in String inputId);
void onInputStateChanged(in String inputId, int state);
void onTvInputInfoUpdated(in TvInputInfo TvInputInfo);
+ void onCurrentTvChannelInfosUpdated(in List<TvChannelInfo> currentTvChannelInfos);
}
diff --git a/media/java/android/media/tv/TvChannelInfo.aidl b/media/java/android/media/tv/TvChannelInfo.aidl
new file mode 100644
index 0000000..71cd0a7
--- /dev/null
+++ b/media/java/android/media/tv/TvChannelInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+parcelable TvChannelInfo;
diff --git a/media/java/android/media/tv/TvChannelInfo.java b/media/java/android/media/tv/TvChannelInfo.java
new file mode 100644
index 0000000..635b130
--- /dev/null
+++ b/media/java/android/media/tv/TvChannelInfo.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+public final class TvChannelInfo implements Parcelable {
+ static final String TAG = "TvChannelInfo";
+ public static final int APP_TAG_SELF = 0;
+ public static final int APP_TYPE_SELF = 1;
+ public static final int APP_TYPE_SYSTEM = 2;
+ public static final int APP_TYPE_NON_SYSTEM = 3;
+
+ /** @hide */
+ @IntDef(prefix = "APP_TYPE_", value = {APP_TYPE_SELF, APP_TYPE_SYSTEM, APP_TYPE_NON_SYSTEM})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AppType {}
+
+ public static final @NonNull Parcelable.Creator<TvChannelInfo> CREATOR =
+ new Parcelable.Creator<TvChannelInfo>() {
+ @Override
+ public TvChannelInfo createFromParcel(Parcel source) {
+ try {
+ return new TvChannelInfo(source);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvChannelInfo from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvChannelInfo[] newArray(int size) {
+ return new TvChannelInfo[size];
+ }
+ };
+
+
+ private final String mInputId;
+ @Nullable private final Uri mChannelUri;
+ private final boolean mIsRecordingSession;
+ private final boolean mIsForeground;
+ @AppType private final int mAppType;
+ private final int mAppTag;
+
+ public TvChannelInfo(
+ String inputId, @Nullable Uri channelUri, boolean isRecordingSession,
+ boolean isForeground, @AppType int appType, int appTag) {
+ mInputId = inputId;
+ mChannelUri = channelUri;
+ mIsRecordingSession = isRecordingSession;
+ mIsForeground = isForeground;
+ mAppType = appType;
+ mAppTag = appTag;
+ }
+
+
+ private TvChannelInfo(Parcel source) {
+ mInputId = source.readString();
+ String uriString = source.readString();
+ mChannelUri = uriString == null ? null : Uri.parse(uriString);
+ mIsRecordingSession = (source.readInt() == 1);
+ mIsForeground = (source.readInt() == 1);
+ mAppType = source.readInt();
+ mAppTag = source.readInt();
+ }
+
+ public String getInputId() {
+ return mInputId;
+ }
+
+ public Uri getChannelUri() {
+ return mChannelUri;
+ }
+
+ public boolean isRecordingSession() {
+ return mIsRecordingSession;
+ }
+
+ public boolean isForeground() {
+ return mIsForeground;
+ }
+
+ /**
+ * Gets app tag.
+ * <p>App tag is used to differentiate one app from another.
+ * {@link #APP_TAG_SELF} is for current app.
+ */
+ public int getAppTag() {
+ return mAppTag;
+ }
+
+ @AppType
+ public int getAppType() {
+ return mAppType;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mInputId);
+ String uriString = mChannelUri == null ? null : mChannelUri.toString();
+ dest.writeString(uriString);
+ dest.writeInt(mIsRecordingSession ? 1 : 0);
+ dest.writeInt(mIsForeground ? 1 : 0);
+ dest.writeInt(mAppType);
+ dest.writeInt(mAppTag);
+ }
+
+ @Override
+ public String toString() {
+ return "inputID=" + mInputId
+ + ";channelUri=" + mChannelUri
+ + ";isRecording=" + mIsRecordingSession
+ + ";isForeground=" + mIsForeground
+ + ";appType=" + mAppType
+ + ";appTag=" + mAppTag;
+ }
+}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 98a01a4..d38369f 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -899,6 +899,10 @@
*/
public void onTvInputInfoUpdated(TvInputInfo inputInfo) {
}
+
+ /** @hide */
+ public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> tvChannelInfos) {
+ }
}
private static final class TvInputCallbackRecord {
@@ -958,6 +962,16 @@
}
});
}
+
+ public void postCurrentTvChannelInfosUpdated(
+ final List<TvChannelInfo> currentTvChannelInfos) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+ }
+ });
+ }
}
/**
@@ -1262,6 +1276,15 @@
}
}
}
+
+ @Override
+ public void onCurrentTvChannelInfosUpdated(List<TvChannelInfo> currentTvChannelInfos) {
+ synchronized (mLock) {
+ for (TvInputCallbackRecord record : mCallbackRecords) {
+ record.postCurrentTvChannelInfosUpdated(currentTvChannelInfos);
+ }
+ }
+ }
};
try {
if (mService != null) {
@@ -1953,6 +1976,17 @@
}
/**
+ * @hide
+ */
+ public List<TvChannelInfo> getCurrentTvChannelInfos() {
+ try {
+ return mService.getCurrentTvChannelInfos(mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* The Session provides the per-session functionality of TV inputs.
* @hide
*/
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index d46f1d1..4d0a01c 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -6138,6 +6138,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
field public static final int FLAG_CANCEL_CURRENT = 268435456; // 0x10000000
field public static final int FLAG_IMMUTABLE = 67108864; // 0x4000000
+ field public static final int FLAG_MUTABLE = 33554432; // 0x2000000
field public static final int FLAG_NO_CREATE = 536870912; // 0x20000000
field public static final int FLAG_ONE_SHOT = 1073741824; // 0x40000000
field public static final int FLAG_UPDATE_CURRENT = 134217728; // 0x8000000
@@ -8783,6 +8784,7 @@
method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
+ method public void onServiceChanged(@NonNull android.bluetooth.BluetoothGatt);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
}
@@ -16490,6 +16492,23 @@
package android.graphics.text {
+ public class GlyphStyle {
+ ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
+ ctor public GlyphStyle(@NonNull android.graphics.Paint);
+ method public void applyToPaint(@NonNull android.graphics.Paint);
+ method @ColorInt public int getColor();
+ method public int getFlags();
+ method @FloatRange(from=0) public float getFontSize();
+ method @FloatRange(from=0) public float getScaleX();
+ method @FloatRange(from=0) public float getSkewX();
+ method public void setColor(@ColorInt int);
+ method public void setFlags(int);
+ method public void setFontSize(@FloatRange(from=0) float);
+ method public void setFromPaint(@NonNull android.graphics.Paint);
+ method public void setScaleX(@FloatRange(from=0) float);
+ method public void setSkewX(@FloatRange(from=0) float);
+ }
+
public class LineBreaker {
method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
@@ -16550,6 +16569,25 @@
method @NonNull public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
}
+ public final class PositionedGlyphs {
+ method public float getAscent();
+ method public float getDescent();
+ method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
+ method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
+ method public float getOriginX();
+ method public float getOriginY();
+ method public float getPositionX(@IntRange(from=0) int);
+ method public float getPositionY(@IntRange(from=0) int);
+ method @NonNull public android.graphics.text.GlyphStyle getStyle();
+ method public float getTotalAdvance();
+ method @IntRange(from=0) public int glyphCount();
+ }
+
+ public class TextShaper {
+ method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
+ method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
+ }
+
}
package android.hardware {
@@ -26316,6 +26354,7 @@
method public boolean containsKey(String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(String);
+ method @IntRange(from=0) public int getBitmapDimensionLimit();
method @NonNull public android.media.MediaDescription getDescription();
method public long getLong(String);
method public android.media.Rating getRating(String);
@@ -26365,6 +26404,7 @@
method public android.media.MediaMetadata.Builder putRating(String, android.media.Rating);
method public android.media.MediaMetadata.Builder putString(String, String);
method public android.media.MediaMetadata.Builder putText(String, CharSequence);
+ method @NonNull public android.media.MediaMetadata.Builder setBitmapDimensionLimit(int);
}
@Deprecated public abstract class MediaMetadataEditor {
@@ -44233,6 +44273,7 @@
field public static final int MISSED = 5; // 0x5
field public static final int OTHER = 9; // 0x9
field public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
+ field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
field public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
field public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
field public static final int REJECTED = 6; // 0x6
@@ -44932,6 +44973,7 @@
field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
field public static final String KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL = "carrier_use_ims_first_for_emergency_bool";
+ field public static final String KEY_CARRIER_USSD_METHOD_INT = "carrier_ussd_method_int";
field public static final String KEY_CARRIER_UT_PROVISIONING_REQUIRED_BOOL = "carrier_ut_provisioning_required_bool";
field public static final String KEY_CARRIER_VOLTE_AVAILABLE_BOOL = "carrier_volte_available_bool";
field public static final String KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL = "carrier_volte_override_wfc_provisioning_bool";
@@ -45126,6 +45168,10 @@
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
+ field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
+ field public static final int USSD_OVER_CS_PREFERRED = 0; // 0x0
+ field public static final int USSD_OVER_IMS_ONLY = 3; // 0x3
+ field public static final int USSD_OVER_IMS_PREFERRED = 1; // 0x1
}
public static final class CarrierConfigManager.Apn {
@@ -45300,9 +45346,9 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellInfoWcdma> CREATOR;
}
- public abstract class CellLocation {
- ctor public CellLocation();
- method public static android.telephony.CellLocation getEmpty();
+ @Deprecated public abstract class CellLocation {
+ ctor @Deprecated public CellLocation();
+ method @Deprecated public static android.telephony.CellLocation getEmpty();
method @Deprecated public static void requestLocationUpdate();
}
@@ -46820,19 +46866,19 @@
package android.telephony.cdma {
- public class CdmaCellLocation extends android.telephony.CellLocation {
- ctor public CdmaCellLocation();
- ctor public CdmaCellLocation(android.os.Bundle);
- method public static double convertQuartSecToDecDegrees(int);
- method public void fillInNotifierBundle(android.os.Bundle);
- method public int getBaseStationId();
- method public int getBaseStationLatitude();
- method public int getBaseStationLongitude();
- method public int getNetworkId();
- method public int getSystemId();
- method public void setCellLocationData(int, int, int);
- method public void setCellLocationData(int, int, int, int, int);
- method public void setStateInvalid();
+ @Deprecated public class CdmaCellLocation extends android.telephony.CellLocation {
+ ctor @Deprecated public CdmaCellLocation();
+ ctor @Deprecated public CdmaCellLocation(android.os.Bundle);
+ method @Deprecated public static double convertQuartSecToDecDegrees(int);
+ method @Deprecated public void fillInNotifierBundle(android.os.Bundle);
+ method @Deprecated public int getBaseStationId();
+ method @Deprecated public int getBaseStationLatitude();
+ method @Deprecated public int getBaseStationLongitude();
+ method @Deprecated public int getNetworkId();
+ method @Deprecated public int getSystemId();
+ method @Deprecated public void setCellLocationData(int, int, int);
+ method @Deprecated public void setCellLocationData(int, int, int, int, int);
+ method @Deprecated public void setStateInvalid();
}
}
@@ -47032,15 +47078,15 @@
package android.telephony.gsm {
- public class GsmCellLocation extends android.telephony.CellLocation {
- ctor public GsmCellLocation();
- ctor public GsmCellLocation(android.os.Bundle);
- method public void fillInNotifierBundle(android.os.Bundle);
- method public int getCid();
- method public int getLac();
- method public int getPsc();
- method public void setLacAndCid(int, int);
- method public void setStateInvalid();
+ @Deprecated public class GsmCellLocation extends android.telephony.CellLocation {
+ ctor @Deprecated public GsmCellLocation();
+ ctor @Deprecated public GsmCellLocation(android.os.Bundle);
+ method @Deprecated public void fillInNotifierBundle(android.os.Bundle);
+ method @Deprecated public int getCid();
+ method @Deprecated public int getLac();
+ method @Deprecated public int getPsc();
+ method @Deprecated public void setLacAndCid(int, int);
+ method @Deprecated public void setStateInvalid();
}
@Deprecated public final class SmsManager {
@@ -48127,6 +48173,10 @@
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
}
+ public class StyledTextShaper {
+ method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
+ }
+
public interface TextDirectionHeuristic {
method public boolean isRtl(char[], int, int);
method public boolean isRtl(CharSequence, int, int);
@@ -51772,6 +51822,33 @@
field public int toolType;
}
+ public interface OnReceiveContentCallback<T extends android.view.View> {
+ method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull T);
+ method public boolean onReceiveContent(@NonNull T, @NonNull android.view.OnReceiveContentCallback.Payload);
+ }
+
+ public static final class OnReceiveContentCallback.Payload {
+ method @NonNull public android.content.ClipData getClip();
+ method @Nullable public android.os.Bundle getExtras();
+ method public int getFlags();
+ method @Nullable public android.net.Uri getLinkUri();
+ method public int getSource();
+ field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+ field public static final int SOURCE_AUTOFILL = 3; // 0x3
+ field public static final int SOURCE_CLIPBOARD = 0; // 0x0
+ field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
+ field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
+ field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
+ }
+
+ public static final class OnReceiveContentCallback.Payload.Builder {
+ ctor public OnReceiveContentCallback.Payload.Builder(@NonNull android.content.ClipData, int);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload build();
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setExtras(@Nullable android.os.Bundle);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setFlags(int);
+ method @NonNull public android.view.OnReceiveContentCallback.Payload.Builder setLinkUri(@Nullable android.net.Uri);
+ }
+
public abstract class OrientationEventListener {
ctor public OrientationEventListener(android.content.Context);
ctor public OrientationEventListener(android.content.Context, int);
@@ -52323,6 +52400,7 @@
method @IdRes public int getNextFocusRightId();
method @IdRes public int getNextFocusUpId();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
+ method @Nullable public android.view.OnReceiveContentCallback<? extends android.view.View> getOnReceiveContentCallback();
method @ColorInt public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
method @ColorInt public int getOutlineSpotShadowColor();
@@ -52674,6 +52752,7 @@
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(@Nullable android.view.View.OnLongClickListener);
+ method public void setOnReceiveContentCallback(@Nullable android.view.OnReceiveContentCallback<? extends android.view.View>);
method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method @Deprecated public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
@@ -58936,17 +59015,6 @@
method public android.view.View newGroupView(android.content.Context, android.database.Cursor, boolean, android.view.ViewGroup);
}
- public interface RichContentReceiver<T extends android.view.View> {
- method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes();
- method public boolean onReceive(@NonNull T, @NonNull android.content.ClipData, int, int);
- field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
- field public static final int SOURCE_AUTOFILL = 3; // 0x3
- field public static final int SOURCE_CLIPBOARD = 0; // 0x0
- field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2
- field public static final int SOURCE_INPUT_METHOD = 1; // 0x1
- field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4
- }
-
public class ScrollView extends android.widget.FrameLayout {
ctor public ScrollView(android.content.Context);
ctor public ScrollView(android.content.Context, android.util.AttributeSet);
@@ -59501,10 +59569,10 @@
method public int getMinWidth();
method public final android.text.method.MovementMethod getMovementMethod();
method public int getOffsetForPosition(float, float);
+ method @Nullable public android.view.OnReceiveContentCallback<android.widget.TextView> getOnReceiveContentCallback();
method public android.text.TextPaint getPaint();
method public int getPaintFlags();
method public String getPrivateImeOptions();
- method @NonNull public android.widget.RichContentReceiver<android.widget.TextView> getRichContentReceiver();
method public int getSelectionEnd();
method public int getSelectionStart();
method @ColorInt public int getShadowColor();
@@ -59632,7 +59700,6 @@
method public void setPaintFlags(int);
method public void setPrivateImeOptions(String);
method public void setRawInputType(int);
- method public void setRichContentReceiver(@NonNull android.widget.RichContentReceiver<android.widget.TextView>);
method public void setScroller(android.widget.Scroller);
method public void setSelectAllOnFocus(boolean);
method public void setShadowLayer(float, float, float, int);
@@ -59673,7 +59740,6 @@
method public void setWidth(int);
field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
- field @NonNull public static final android.widget.RichContentReceiver<android.widget.TextView> DEFAULT_RICH_CONTENT_RECEIVER;
}
public enum TextView.BufferType {
@@ -59690,6 +59756,12 @@
field @NonNull public static final android.os.Parcelable.Creator<android.widget.TextView.SavedState> CREATOR;
}
+ public class TextViewOnReceiveContentCallback implements android.view.OnReceiveContentCallback<android.widget.TextView> {
+ ctor public TextViewOnReceiveContentCallback();
+ method @NonNull public java.util.Set<java.lang.String> getSupportedMimeTypes(@NonNull android.widget.TextView);
+ method public boolean onReceiveContent(@NonNull android.widget.TextView, @NonNull android.view.OnReceiveContentCallback.Payload);
+ }
+
public interface ThemedSpinnerAdapter extends android.widget.SpinnerAdapter {
method @Nullable public android.content.res.Resources.Theme getDropDownViewTheme();
method public void setDropDownViewTheme(@Nullable android.content.res.Resources.Theme);
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index 6c0dd33..11cd6a9 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -16,6 +16,14 @@
}
+package android.app.role {
+
+ public final class RoleManager {
+ method @Nullable public String getDefaultSmsPackage(int);
+ }
+
+}
+
package android.content.rollback {
public class RollbackManagerFrameworkInitializer {
@@ -45,10 +53,6 @@
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
- public static final class MediaMetadata.Builder {
- ctor public MediaMetadata.Builder(@NonNull android.media.MediaMetadata, @IntRange(from=1) int);
- }
-
}
package android.media.session {
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 746f8aa..916e21f 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -9303,10 +9303,6 @@
method public final void addExistingConnection(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.telecom.Connection, @NonNull android.telecom.Conference);
}
- public final class DisconnectCause implements android.os.Parcelable {
- field public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
- }
-
public abstract class InCallService extends android.app.Service {
method @Deprecated public android.telecom.Phone getPhone();
method @Deprecated public void onPhoneCreated(android.telecom.Phone);
diff --git a/packages/CarSystemUI/res/drawable/car_ic_settings_icon.xml b/packages/CarSystemUI/res/drawable/car_ic_settings_icon.xml
new file mode 100644
index 0000000..147f539
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/car_ic_settings_icon.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="@*android:dimen/status_bar_system_icon_size"
+ android:height="@*android:dimen/status_bar_system_icon_size"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@color/system_bar_icon_color"
+ android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4C9.75,2 9.54,2.18 9.51,2.42L9.13,5.07C8.52,5.32 7.96,5.66 7.44,6.05l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46C2.21,8.95 2.27,9.22 2.46,9.37l2.11,1.65C4.53,11.34 4.5,11.67 4.5,12s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65C9.54,21.82 9.75,22 10,22h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64L19.43,12.98zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5s3.5,1.57 3.5,3.5S13.93,15.5 12,15.5z"/>
+</vector>
diff --git a/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml b/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml
index 1195d05..270d932 100644
--- a/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_user_icon.xml
@@ -21,5 +21,5 @@
android:viewportHeight="24">
<path
android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"
- android:fillColor="@color/system_bar_user_icon_color"/>
+ android:fillColor="@color/system_bar_icon_color"/>
</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
index 07c11c7..7994b19 100644
--- a/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar.xml
@@ -36,6 +36,7 @@
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_marginTop="@dimen/car_padding_2"
+ android:layout_marginStart="@dimen/car_padding_2"
android:layout_centerVertical="true"
android:gravity="center_vertical"
>
@@ -44,7 +45,6 @@
android:layout_height="match_parent"
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">
@@ -53,6 +53,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:layout_marginEnd="@dimen/car_padding_2"
android:gravity="center_vertical"
/>
</com.android.systemui.car.navigationbar.CarNavigationButton>
diff --git a/packages/CarSystemUI/res/layout/system_icons.xml b/packages/CarSystemUI/res/layout/system_icons.xml
index 5c06075..f6ffcc8 100644
--- a/packages/CarSystemUI/res/layout/system_icons.xml
+++ b/packages/CarSystemUI/res/layout/system_icons.xml
@@ -20,16 +20,24 @@
android:id="@+id/system_icons"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center_vertical">
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
<com.android.systemui.statusbar.phone.StatusIconContainer
android:id="@+id/statusIcons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
- android:padding="10dp"
android:scaleType="fitCenter"
android:gravity="center_vertical"
android:orientation="horizontal"
/>
+
+ <ImageView
+ android:id="@+id/settingsIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/car_padding_2"
+ android:src="@drawable/car_ic_settings_icon"
+ />
</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 6fe5004..0181b9a 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -33,7 +33,7 @@
<!-- colors for status bar -->
<color name="system_bar_background_pill_color">#282A2D</color>
- <color name="system_bar_user_icon_color">#FFFFFF</color>
+ <color name="system_bar_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 fe060ac..86bfa75 100644
--- a/packages/CarSystemUI/res/values/dimens.xml
+++ b/packages/CarSystemUI/res/values/dimens.xml
@@ -46,6 +46,10 @@
in frameworks/base/core package and thus will have no effect if
set here. See car_product overlay for car specific defaults-->
+ <!-- Overrides the space between each status icon in the system bar -->
+ <dimen name="status_bar_system_icon_spacing">16dp</dimen>
+ <!-- Overrides the size of the network signal icon -->
+ <dimen name="signal_icon_size">32dp</dimen>
<dimen name="system_bar_user_icon_padding">16dp</dimen>
<dimen name="system_bar_user_icon_drawing_size">36dp</dimen>
<!-- Padding on either side of the group of all system bar buttons -->
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
index b113d29..f2ca495 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -50,6 +50,7 @@
private final Context mContext;
private final DisplayController mDisplayController;
+ private final Handler mHandler;
private SparseArray<PerDisplay> mPerDisplaySparseArray;
public DisplaySystemBarsController(
@@ -58,9 +59,10 @@
DisplayController displayController,
@Main Handler mainHandler,
TransactionPool transactionPool) {
- super(wmService, displayController, mainHandler, transactionPool);
+ super(wmService, displayController, (r) -> mainHandler.post(r), transactionPool);
mContext = context;
mDisplayController = displayController;
+ mHandler = mainHandler;
}
@Override
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index 3b00576..bf508b2 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/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-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index 1929278..f387414 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/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-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 99aed5e..6b4f7ebf 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švýcarské (němč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ávesnice"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italské"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"dánské"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norské"</string>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 697fff6..45aca35 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Allemand (Suisse)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgare"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Phonétique bulgare"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italien"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danois"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvégien"</string>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index 2e14019..b55a3c9 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Allemand (Suisse)"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgare"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Phonétique bulgare"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italien"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danois"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norvégien"</string>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 2ffebdd..7d1e2f8 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/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-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index cfdc3f8..dfe8c56 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/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-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index 2aaf816c7..3bd7f20 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/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-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index c036409..9584e15 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/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-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index 24816c1..4801f75 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/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-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index 0cb4f34..7066813 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -27,7 +27,7 @@
<string name="keyboard_layout_finnish" msgid="5585659438924315466">"финский"</string>
<string name="keyboard_layout_croatian" msgid="4172229471079281138">"хорватский"</string>
<string name="keyboard_layout_czech" msgid="1349256901452975343">"чешский"</string>
- <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Чешский (QWERTY)"</string>
+ <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"чешский (QWERTY)"</string>
<string name="keyboard_layout_estonian" msgid="8775830985185665274">"эстонский"</string>
<string name="keyboard_layout_hungarian" msgid="4154963661406035109">"венгерский"</string>
<string name="keyboard_layout_icelandic" msgid="5836645650912489642">"исландский"</string>
@@ -44,9 +44,9 @@
<string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"испанский (Латинская Америка)"</string>
<string name="keyboard_layout_latvian" msgid="4405417142306250595">"латышский"</string>
<string name="keyboard_layout_persian" msgid="3920643161015888527">"Персидский"</string>
- <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"Азербайджанский"</string>
+ <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"азербайджанский"</string>
<string name="keyboard_layout_polish" msgid="1121588624094925325">"польский"</string>
- <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Белорусский"</string>
- <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Монгольский"</string>
- <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинский"</string>
+ <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"белорусский"</string>
+ <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"монгольский"</string>
+ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузинский"</string>
</resources>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index b3677cd..9b4fe9e 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"gjermanishte zvicerane"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"belge"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bullgarisht"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tastierë bullgare, fonetike"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"italisht"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"danisht"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norvegjisht"</string>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index b75b57d..d3c6000 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/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-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index 21ea5de..b9aee76 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -19,8 +19,7 @@
<string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"Swiss German"</string>
<string name="keyboard_layout_belgian" msgid="2011984572838651558">"Belgian"</string>
<string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"Bulgarian"</string>
- <!-- no translation found for keyboard_layout_bulgarian_phonetic (7568914730360106653) -->
- <skip />
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Bulgarian, Phonetic"</string>
<string name="keyboard_layout_italian" msgid="6497079660449781213">"Italian"</string>
<string name="keyboard_layout_danish" msgid="8036432066627127851">"Danish"</string>
<string name="keyboard_layout_norwegian" msgid="9090097917011040937">"Norwegian"</string>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index 1b42ece..76fd0bf 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -19,7 +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>
- <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Ngữ âm"</string>
+ <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"Tiếng Bulgaria, Phiên â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/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 696ed29..f13d9b8 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -538,7 +538,7 @@
<string name="user_setup_button_setup_now" msgid="1708269547187760639">"지금 설정"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"나중에"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"추가"</string>
- <string name="user_new_user_name" msgid="60979820612818840">"새 사용자"</string>
+ <string name="user_new_user_name" msgid="60979820612818840">"신규 사용자"</string>
<string name="user_new_profile_name" msgid="2405500423304678841">"새 프로필"</string>
<string name="user_info_settings_title" msgid="6351390762733279907">"사용자 정보"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"프로필 정보"</string>
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 9788b30..0810979 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -437,6 +437,7 @@
Settings.Global.SHOW_MUTE_IN_CRASH_DIALOG,
Settings.Global.SHOW_NEW_APP_INSTALLED_NOTIFICATION_ENABLED,
Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
+ Settings.Global.SHOW_PEOPLE_SPACE,
Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG,
Settings.Global.SHOW_TEMPERATURE_WARNING,
Settings.Global.SHOW_USB_TEMPERATURE_ALARM,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index ce427cb..0eac4ad 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -511,14 +511,14 @@
}
if (msg.what != MSG_SERVICE_COMMAND) {
- // Sanity check.
+ // Confidence check.
Log.e(TAG, "Invalid message type: " + msg.what);
return;
}
// At this point it's handling onStartCommand(), with the intent passed as an Extra.
if (!(msg.obj instanceof Intent)) {
- // Sanity check.
+ // Confidence check.
Log.wtf(TAG, "handleMessage(): invalid msg.obj type: " + msg.obj);
return;
}
diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
index cd3cad1..3b02e3b 100644
--- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
+++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java
@@ -466,7 +466,7 @@
// Clear properties
mContext.getSharedPreferences(PREFS_BUGREPORT, Context.MODE_PRIVATE)
.edit().clear().commit();
- // Sanity check...
+ // Confidence check...
assertEquals("Did not reset properties", STATE_UNKNOWN,
getWarningState(mContext, STATE_UNKNOWN));
} else {
diff --git a/packages/SystemUI/res/layout/media_output_list_item.xml b/packages/SystemUI/res/layout/media_output_list_item.xml
index 92d0858..ac8b7b5 100644
--- a/packages/SystemUI/res/layout/media_output_list_item.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item.xml
@@ -75,18 +75,6 @@
android:textSize="12sp"
android:fontFamily="roboto-regular"
android:visibility="gone"/>
- <ProgressBar
- android:id="@+id/volume_indeterminate_progress"
- style="@*android:style/Widget.Material.ProgressBar.Horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="15dp"
- android:layout_marginBottom="1dp"
- android:layout_alignParentBottom="true"
- android:indeterminate="true"
- android:indeterminateOnly="true"
- android:visibility="gone"/>
<SeekBar
android:id="@+id/volume_seekbar"
android:layout_width="match_parent"
@@ -94,6 +82,17 @@
android:layout_alignParentBottom="true"/>
</RelativeLayout>
+ <ProgressBar
+ android:id="@+id/volume_indeterminate_progress"
+ style="@*android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="258dp"
+ android:layout_height="18dp"
+ android:layout_marginStart="68dp"
+ android:layout_marginTop="40dp"
+ android:indeterminate="true"
+ android:indeterminateOnly="true"
+ android:visibility="gone"/>
+
<View
android:layout_width="1dp"
android:layout_height="36dp"
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index e1d1950..b09e5b9 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -899,9 +899,9 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string>
<string name="other" msgid="429768510980739978">"غير ذلك"</string>
- <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_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>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index fcdd286..aed9522 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/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-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 783f7877..0a03680 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -880,9 +880,9 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string>
<string name="other" msgid="429768510980739978">"Altres"</string>
<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_add_action" msgid="5051211910345301833">"afegir un mosaic al final"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou el mosaic"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Afegeix un mosaic"</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>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 3f3d6be..419e625 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -889,20 +889,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Zobrazit ikony oznámení s nízkou prioritou"</string>
<string name="other" msgid="429768510980739978">"Jiné"</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">"odstranit dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"přidat dlaždici na konec"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Přesunout dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Přidat dlaždici"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Přesunout na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozice <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editor rychlého nastavení"</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Oznámení aplikace <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">"Otevřít nastavení."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 887aad5..4dbd74a 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -28,15 +28,15 @@
<string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string>
<string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Noch <xliff:g id="PERCENTAGE">%1$s</xliff:g> übrig; bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string>
- <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string>
+ <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Stromsparmodus ist aktiviert."</string>
<string name="invalid_charger" msgid="4370074072117767416">"Aufladen über USB nicht möglich. Verwende das mit dem Gerät gelieferte Ladegerät."</string>
<string name="invalid_charger_title" msgid="938685362320735167">"Aufladen über USB nicht möglich"</string>
<string name="invalid_charger_text" msgid="2339310107232691577">"Verwende das mit dem Gerät gelieferte Ladegerät"</string>
<string name="battery_low_why" msgid="2056750982959359863">"Einstellungen"</string>
- <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Energiesparmodus aktivieren?"</string>
- <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Energiesparmodus"</string>
+ <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Stromsparmodus aktivieren?"</string>
+ <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Stromsparmodus"</string>
<string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Aktivieren"</string>
- <string name="battery_saver_start_action" msgid="4553256017945469937">"Energiesparmodus aktivieren"</string>
+ <string name="battery_saver_start_action" msgid="4553256017945469937">"Stromsparmodus aktivieren"</string>
<string name="status_bar_settings_settings_button" msgid="534331565185171556">"Einstellungen"</string>
<string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WLAN"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Bildschirm automatisch drehen"</string>
@@ -419,7 +419,7 @@
<string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Bis <xliff:g id="TIME">%s</xliff:g>"</string>
<string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Dunkles Design"</string>
- <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string>
+ <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Stromsparmodus"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string>
<string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string>
<string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -497,9 +497,9 @@
<string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string>
<string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string>
- <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string>
+ <string name="battery_saver_notification_title" msgid="8419266546034372562">"Stromsparmodus ist aktiviert"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string>
- <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string>
+ <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Stromsparmodus deaktivieren"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string>
@@ -773,8 +773,8 @@
<item quantity="one">%d Minute</item>
</plurals>
<string name="battery_panel_title" msgid="5931157246673665963">"Akkunutzung"</string>
- <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string>
- <string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string>
+ <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Stromsparmodus ist beim Aufladen nicht verfügbar."</string>
+ <string name="battery_detail_switch_title" msgid="6940976502957380405">"Stromsparmodus"</string>
<string name="battery_detail_switch_summary" msgid="3668748557848025990">"Reduzierung der Leistung und Hintergrunddaten"</string>
<string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
@@ -961,11 +961,11 @@
<string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> darf Teile aus jeder beliebigen App anzeigen"</string>
<string name="slice_permission_allow" msgid="6340449521277951123">"Zulassen"</string>
<string name="slice_permission_deny" msgid="6870256451658176895">"Ablehnen"</string>
- <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string>
+ <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Stromsparmodus"</string>
<string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string>
- <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Energiesparmodus aktiviert"</string>
- <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
+ <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Stromsparmodus aktiviert"</string>
+ <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Stromsparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string>
<string name="open_saver_setting_action" msgid="2111461909782935190">"Einstellungen"</string>
<string name="auto_saver_okay_action" msgid="7815925750741935386">"Ok"</string>
<string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index dfd42c3..8a8f1e7 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -246,7 +246,7 @@
<string name="accessibility_remove_notification" msgid="1641455251495815527">"Eliminar notificación"</string>
<string name="accessibility_gps_enabled" msgid="4061313248217660858">"GPS habilitado"</string>
<string name="accessibility_gps_acquiring" msgid="896207402196024040">"Adquisición de GPS"</string>
- <string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter habilitado"</string>
+ <string name="accessibility_tty_enabled" msgid="1123180388823381118">"Teletipo habilitado"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Timbre en vibración"</string>
<string name="accessibility_ringer_silent" msgid="8994620163934249882">"Timbre en silencio"</string>
<!-- no translation found for accessibility_casting (8708751252897282313) -->
@@ -879,8 +879,8 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string>
<string name="other" msgid="429768510980739978">"Otros"</string>
- <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_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>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index c1ab20c..e8d2f47 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -881,11 +881,11 @@
<string name="other" msgid="429768510980739978">"Beste bat"</string>
<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_move" msgid="2009373939914517817">"Mugitu 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_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren lekura"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren lekuan"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"<xliff:g id="POSITION">%1$d</xliff:g>garren lekua"</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-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index ea6de66..ff89ce2 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification de faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</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">"retirer la tuile"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la tuile à la fin"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la tuile"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter la tuile"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la 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">"Éditeur de paramètres rapides."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <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">"Ouvrir les paramètres."</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index fc2691e3..a126cfa 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification à faible priorité"</string>
<string name="other" msgid="429768510980739978">"Autre"</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">"supprimer la carte"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"ajouter la carte à la fin"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la carte"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Ajouter une carte"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la 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">"Éditeur de configuration rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notification <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">"Ouvrir les paramètres."</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 1a69e81..9a563d9 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -881,20 +881,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-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 28c79fb..171773c 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -880,7 +880,7 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string>
<string name="other" msgid="429768510980739978">"Այլ"</string>
<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_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>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 1f8f2aa..4a750a5 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -879,10 +879,10 @@
</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>
- <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_remove_tile_action" msgid="775511891457193480">"fjarlægja flís"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"bæta flís við aftast"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa flís"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Bæta flís 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>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 31e6bef..e6fbdb5 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -890,10 +890,10 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string>
<string name="other" msgid="429768510980739978">"אחר"</string>
<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_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_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>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index d775823..b1fab07 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -879,10 +879,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
<string name="other" msgid="429768510980739978">"Башка"</string>
- <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_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>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index fc68681..80f0106 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -883,8 +883,8 @@
<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_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>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 66e3ec3..7584173 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -889,10 +889,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string>
<string name="other" msgid="429768510980739978">"Другое"</string>
- <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_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>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index abcde42..5172303 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -879,20 +879,13 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Shfaq ikonat e njoftimeve me përparësi të ulët"</string>
<string name="other" msgid="429768510980739978">"Të tjera"</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">"hiq pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"shto pllakëzën në fund"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Zhvendos pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Shto pllakëzën"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Zhvendos te <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Shto te pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Redaktori i cilësimeve të shpejta."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Njoftim nga <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">"Hap cilësimet."</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 9d5c54f..d91f1fd 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/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-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index eeedac6..26053af 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -889,10 +889,10 @@
</string-array>
<string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string>
<string name="other" msgid="429768510980739978">"Інше"</string>
- <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_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>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index facb47a..dbb9e03 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -880,8 +880,8 @@
<string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
<string name="other" msgid="429768510980739978">"其他"</string>
<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_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>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 48da5d9..8777d50 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -506,6 +506,10 @@
<dimen name="qs_header_tile_margin_bottom">18dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
+ <!-- The size of a single dot in relation to the whole animation.
+ Scaled @dimen/qs_page_indicator-width by .4f.
+ -->
+ <dimen name="qs_page_indicator_dot_width">6.4dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
<dimen name="qs_tile_text_size">12sp</dimen>
<dimen name="qs_tile_divider_height">1dp</dimen>
@@ -1376,4 +1380,5 @@
<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>
+ <dimen name="media_output_dialog_title_anim_y_delta">12.5dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index d815681..f2bb490 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -175,5 +175,13 @@
<!-- Accessibility actions for PIP -->
<item type="id" name="action_pip_resize" />
+
+ <!-- Accessibility actions for window magnification. -->
+ <item type="id" name="accessibility_action_zoom_in"/>
+ <item type="id" name="accessibility_action_zoom_out"/>
+ <item type="id" name="accessibility_action_move_left"/>
+ <item type="id" name="accessibility_action_move_right"/>
+ <item type="id" name="accessibility_action_move_up"/>
+ <item type="id" name="accessibility_action_move_down"/>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index f693149..4ba757f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2666,6 +2666,18 @@
<string name="magnification_window_title">Magnification Window</string>
<!-- Title for Magnification Controls Window [CHAR LIMIT=NONE] -->
<string name="magnification_controls_title">Magnification Window Controls</string>
+ <!-- Action in accessibility menu to zoom in content of the magnification window. [CHAR LIMIT=30] -->
+ <string name="accessibility_control_zoom_in">Zoom in</string>
+ <!-- Action in accessibility menu to zoom out content of the magnification window. [CHAR LIMIT=30] -->
+ <string name="accessibility_control_zoom_out">Zoom out</string>
+ <!-- Action in accessibility menu to move the magnification window up. [CHAR LIMIT=30] -->
+ <string name="accessibility_control_move_up">Move up</string>
+ <!-- Action in accessibility menu to move the magnification window down. [CHAR LIMIT=30] -->
+ <string name="accessibility_control_move_down">Move down</string>
+ <!-- Action in accessibility menu to move the magnification window left. [CHAR LIMIT=30] -->
+ <string name="accessibility_control_move_left">Move left</string>
+ <!-- Action in accessibility menu to move the magnification window right. [CHAR LIMIT=30] -->
+ <string name="accessibility_control_move_right">Move right</string>
<!-- Device Controls strings -->
<!-- Device Controls empty state, title [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index cffc10f..ee05c6c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -373,19 +373,6 @@
}
/**
- * Moves an already resumed task to the side of the screen to initiate split screen.
- */
- public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode,
- Rect initialBounds) {
- try {
- return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(taskId,
- true /* onTop */);
- } catch (RemoteException e) {
- return false;
- }
- }
-
- /**
* Registers a task stack listener with the system.
* This should be called on the main thread.
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 2985a61..86129e0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -47,6 +47,7 @@
public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5;
public static final int FLAG_VISIBILITY = 1 << 6;
public static final int FLAG_RELATIVE_LAYER = 1 << 7;
+ public static final int FLAG_SHADOW_RADIUS = 1 << 8;
private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
@@ -196,6 +197,7 @@
SurfaceControl relativeTo;
int relativeLayer;
boolean visible;
+ float shadowRadius;
/**
* @param surface The surface to modify.
@@ -274,6 +276,16 @@
}
/**
+ * @param radius the Radius for the shadows to apply to the surface.
+ * @return this Builder
+ */
+ public Builder withShadowRadius(float radius) {
+ this.shadowRadius = radius;
+ flags |= FLAG_SHADOW_RADIUS;
+ return this;
+ }
+
+ /**
* @param radius the Radius for blur to apply to the background surfaces.
* @return this Builder
*/
@@ -298,31 +310,14 @@
*/
public SurfaceParams build() {
return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer,
- relativeTo, relativeLayer, cornerRadius, backgroundBlurRadius, visible);
+ relativeTo, relativeLayer, cornerRadius, backgroundBlurRadius, visible,
+ shadowRadius);
}
}
- /**
- * Constructs surface parameters to be applied when the current view state gets pushed to
- * RenderThread.
- *
- * @param surface The surface to modify.
- * @param alpha Alpha to apply.
- * @param matrix Matrix to apply.
- * @param windowCrop Crop to apply, only applied if not {@code null}
- */
- public SurfaceParams(SurfaceControlCompat surface, float alpha, Matrix matrix,
- Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
- float cornerRadius) {
- this(surface.mSurfaceControl,
- FLAG_ALL & ~(FLAG_VISIBILITY | FLAG_BACKGROUND_BLUR_RADIUS), alpha,
- matrix, windowCrop, layer, relativeTo, relativeLayer, cornerRadius,
- 0 /* backgroundBlurRadius */, true);
- }
-
private SurfaceParams(SurfaceControl surface, int flags, float alpha, Matrix matrix,
Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
- float cornerRadius, int backgroundBlurRadius, boolean visible) {
+ float cornerRadius, int backgroundBlurRadius, boolean visible, float shadowRadius) {
this.flags = flags;
this.surface = surface;
this.alpha = alpha;
@@ -334,6 +329,7 @@
this.cornerRadius = cornerRadius;
this.backgroundBlurRadius = backgroundBlurRadius;
this.visible = visible;
+ this.shadowRadius = shadowRadius;
}
private final int flags;
@@ -349,6 +345,7 @@
public final SurfaceControl relativeTo;
public final int relativeLayer;
public final boolean visible;
+ public final float shadowRadius;
public void applyTo(SurfaceControl.Transaction t) {
if ((flags & FLAG_MATRIX) != 0) {
@@ -379,6 +376,9 @@
if ((flags & FLAG_RELATIVE_LAYER) != 0) {
t.setRelativeLayer(surface, relativeTo, relativeLayer);
}
+ if ((flags & FLAG_SHADOW_RADIUS) != 0) {
+ t.setShadowRadius(surface, shadowRadius);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 7140956..3832ff30 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -30,9 +30,11 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
+import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Range;
import android.view.Choreographer;
import android.view.Display;
import android.view.Gravity;
@@ -47,12 +49,17 @@
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.R;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.text.NumberFormat;
+import java.util.Locale;
+
/**
* Class to handle adding and removing a window magnification.
*/
@@ -60,6 +67,11 @@
MirrorWindowControl.MirrorWindowDelegate {
private static final String TAG = "WindowMagnificationController";
+ // Delay to avoid updating state description too frequently.
+ private static final int UPDATE_STATE_DESCRIPTION_DELAY_MS = 100;
+ // It should be consistent with the value defined in WindowMagnificationGestureHandler.
+ private static final Range<Float> A11Y_ACTION_SCALE_RANGE = new Range<>(2.0f, 8.0f);
+ private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f;
private final Context mContext;
private final Resources mResources;
private final Handler mHandler;
@@ -95,6 +107,7 @@
private final View.OnLayoutChangeListener mMirrorViewLayoutChangeListener;
private final View.OnLayoutChangeListener mMirrorSurfaceViewLayoutChangeListener;
private final Runnable mMirrorViewRunnable;
+ private final Runnable mUpdateStateDescriptionRunnable;
private View mMirrorView;
private SurfaceView mMirrorSurfaceView;
private int mMirrorSurfaceMargin;
@@ -106,6 +119,8 @@
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
private Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback;
+ private Locale mLocale;
+ private NumberFormat mPercentFormat;
@Nullable
private MirrorWindowControl mMirrorWindowControl;
@@ -164,6 +179,11 @@
mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, mSourceBounds);
}
};
+ mUpdateStateDescriptionRunnable = () -> {
+ if (isWindowVisible()) {
+ mMirrorView.setStateDescription(formatStateDescription(mScale));
+ }
+ };
}
private void updateDimensions() {
@@ -292,12 +312,13 @@
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
mMirrorView.addOnLayoutChangeListener(mMirrorViewLayoutChangeListener);
+ mMirrorView.setAccessibilityDelegate(new MirrorWindowA11yDelegate());
+
mWm.addView(mMirrorView, params);
SurfaceHolder holder = mMirrorSurfaceView.getHolder();
holder.addCallback(this);
holder.setFormat(PixelFormat.RGBA_8888);
-
addDragTouchListeners();
}
@@ -526,6 +547,7 @@
final float offsetY = Float.isNaN(centerY) ? 0
: centerY - mMagnificationFrame.exactCenterY();
mScale = Float.isNaN(scale) ? mScale : scale;
+
setMagnificationFrameBoundary();
updateMagnificationFramePosition((int) offsetX, (int) offsetY);
if (!isWindowVisible()) {
@@ -546,6 +568,8 @@
return;
}
enableWindowMagnification(scale, Float.NaN, Float.NaN);
+ mHandler.removeCallbacks(mUpdateStateDescriptionRunnable);
+ mHandler.postDelayed(mUpdateStateDescriptionRunnable, UPDATE_STATE_DESCRIPTION_DELAY_MS);
}
/**
@@ -596,4 +620,78 @@
private boolean isWindowVisible() {
return mMirrorView != null;
}
+
+ private CharSequence formatStateDescription(float scale) {
+ // Cache the locale-appropriate NumberFormat. Configuration locale is guaranteed
+ // non-null, so the first time this is called we will always get the appropriate
+ // NumberFormat, then never regenerate it unless the locale changes on the fly.
+ final Locale curLocale = mContext.getResources().getConfiguration().getLocales().get(0);
+ if (!curLocale.equals(mLocale)) {
+ mLocale = curLocale;
+ mPercentFormat = NumberFormat.getPercentInstance(curLocale);
+ }
+ return mPercentFormat.format(scale);
+ }
+
+ private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ info.addAction(
+ new AccessibilityAction(R.id.accessibility_action_zoom_in,
+ mContext.getString(R.string.accessibility_control_zoom_in)));
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_zoom_out,
+ mContext.getString(R.string.accessibility_control_zoom_out)));
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_move_up,
+ mContext.getString(R.string.accessibility_control_move_up)));
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_move_down,
+ mContext.getString(R.string.accessibility_control_move_down)));
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_move_left,
+ mContext.getString(R.string.accessibility_control_move_left)));
+ info.addAction(new AccessibilityAction(R.id.accessibility_action_move_right,
+ mContext.getString(R.string.accessibility_control_move_right)));
+
+ info.setContentDescription(mContext.getString(R.string.magnification_window_title));
+ info.setStateDescription(formatStateDescription(getScale()));
+ }
+
+ @Override
+ public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ if (performA11yAction(action)) {
+ return true;
+ }
+ return super.performAccessibilityAction(host, action, args);
+ }
+
+ private boolean performA11yAction(int action) {
+ if (action == R.id.accessibility_action_zoom_in) {
+ final float scale = mScale + A11Y_CHANGE_SCALE_DIFFERENCE;
+ setScale(A11Y_ACTION_SCALE_RANGE.clamp(scale));
+ return true;
+ }
+ if (action == R.id.accessibility_action_zoom_out) {
+ final float scale = mScale - A11Y_CHANGE_SCALE_DIFFERENCE;
+ setScale(A11Y_ACTION_SCALE_RANGE.clamp(scale));
+ return true;
+ }
+ if (action == R.id.accessibility_action_move_up) {
+ move(0, -mSourceBounds.height());
+ return true;
+ }
+ if (action == R.id.accessibility_action_move_down) {
+ move(0, mSourceBounds.height());
+ return true;
+ }
+ if (action == R.id.accessibility_action_move_left) {
+ move(-mSourceBounds.width(), 0);
+ return true;
+ }
+ if (action == R.id.accessibility_action_move_right) {
+ move(mSourceBounds.width(), 0);
+ return true;
+ }
+ return false;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index d79c96e..289ffbe 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -16,6 +16,7 @@
package com.android.systemui.biometrics;
+import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.SuppressLint;
@@ -25,6 +26,7 @@
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.PowerManager;
import android.os.UserHandle;
@@ -41,6 +43,7 @@
import androidx.annotation.Nullable;
import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
@@ -50,13 +53,22 @@
import java.io.FileWriter;
import java.io.IOException;
+import java.util.List;
import javax.inject.Inject;
/**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
* and coordinates triggering of the high-brightness mode (HBM).
+ *
+ * Note that the current architecture is designed so that a single {@link UdfpsController}
+ * controls/manages all UDFPS sensors. In other words, a single controller is registered with
+ * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such
+ * as {@link FingerprintManager#onFingerDown(int, int, int, float, float)} or
+ * {@link IUdfpsOverlayController#showUdfpsOverlay(int)}should all have
+ * {@code sensorId} parameters.
*/
+@SuppressWarnings("deprecation")
class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController";
// Gamma approximation for the sRGB color space.
@@ -64,6 +76,10 @@
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
private final FingerprintManager mFingerprintManager;
+ // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
+ // sensors, this, in addition to a lot of the code here, will be updated.
+ @VisibleForTesting
+ final int mUdfpsSensorId;
private final WindowManager mWindowManager;
private final SystemSettings mSystemSettings;
private final DelayableExecutor mFgExecutor;
@@ -103,17 +119,17 @@
public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
@Override
- public void showUdfpsOverlay() {
+ public void showUdfpsOverlay(int sensorId) {
UdfpsController.this.setShowOverlay(true);
}
@Override
- public void hideUdfpsOverlay() {
+ public void hideUdfpsOverlay(int sensorId) {
UdfpsController.this.setShowOverlay(false);
}
@Override
- public void setDebugMessage(String message) {
+ public void setDebugMessage(int sensorId, String message) {
mView.setDebugMessage(message);
}
}
@@ -165,6 +181,17 @@
mFgExecutor = fgExecutor;
mLayoutParams = createLayoutParams(context);
+ int udfpsSensorId = -1;
+ for (FingerprintSensorProperties props : mFingerprintManager.getSensorProperties()) {
+ if (props.isAnyUdfpsType()) {
+ udfpsSensorId = props.sensorId;
+ break;
+ }
+ }
+ // At least one UDFPS sensor exists
+ checkArgument(udfpsSensorId != -1);
+ mUdfpsSensorId = udfpsSensorId;
+
mView = (UdfpsView) inflater.inflate(R.layout.udfps_view, null, false);
mHbmPath = resources.getString(R.string.udfps_hbm_sysfs_path);
@@ -347,7 +374,7 @@
fw.write(mHbmEnableCommand);
fw.close();
}
- mFingerprintManager.onFingerDown(x, y, minor, major);
+ mFingerprintManager.onFingerDown(mUdfpsSensorId, x, y, minor, major);
} catch (IOException e) {
mView.hideScrimAndDot();
Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
@@ -355,7 +382,7 @@
}
private void onFingerUp() {
- mFingerprintManager.onFingerUp();
+ mFingerprintManager.onFingerUp(mUdfpsSensorId);
// Hiding the scrim before disabling HBM results in less noticeable flicker.
mView.hideScrimAndDot();
if (mHbmSupported) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index e835ea2..46ef9bc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -74,7 +74,7 @@
*/
public static final int DEFAULT_STIFFNESS = 12000;
public static final float IME_ANIMATION_STIFFNESS = SpringForce.STIFFNESS_LOW;
- private static final int FLING_FOLLOW_STIFFNESS = 20000;
+ private static final int FLING_FOLLOW_STIFFNESS = 500;
public static final float DEFAULT_BOUNCINESS = 0.9f;
private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfig =
diff --git a/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
new file mode 100644
index 0000000..dccb24d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/emergency/EmergencyGesture.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.emergency;
+
+/**
+ * Constants for the Emergency gesture.
+ *
+ * TODO (b/169175022) Update classname and docs when feature name is locked
+ */
+public final class EmergencyGesture {
+
+ /**
+ * Launches the emergency flow.
+ *
+ * <p>The emergency flow is triggered by the Emergency gesture. By default the flow will call
+ * local emergency services, though OEMs can customize the flow.
+ *
+ * <p>This action can only be triggered by System UI through the emergency gesture.
+ *
+ * <p>TODO (b/169175022) Update action name and docs when feature name is locked
+ */
+ public static final String ACTION_LAUNCH_EMERGENCY =
+ "com.android.systemui.action.LAUNCH_EMERGENCY";
+
+ private EmergencyGesture() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index daef2c5..5f726cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -38,6 +38,7 @@
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.text.style.StyleSpan;
+import android.util.Log;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -52,6 +53,8 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIAppComponentFactory;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.dagger.SysUIComponent;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarState;
@@ -62,6 +65,8 @@
import com.android.systemui.util.wakelock.SettableWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
@@ -80,6 +85,8 @@
NotificationMediaManager.MediaListener, StatusBarStateController.StateListener,
SystemUIAppComponentFactory.ContextInitializer {
+ private static final String TAG = "KgdSliceProvider";
+
private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
private static final String KEYGUARD_HEADER_URI =
@@ -310,7 +317,25 @@
mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
mPendingIntent = PendingIntent.getActivity(getContext(), 0,
new Intent(getContext(), KeyguardSliceProvider.class), 0);
- mMediaManager.addCallback(this);
+ try {
+ //TODO(b/168778439): Remove this whole try catch. This is for debugging in dogfood.
+ mMediaManager.addCallback(this);
+ } catch (NullPointerException e) {
+ // We are sometimes failing to set the media manager. Why?
+ Log.w(TAG, "Failed to setup mMediaManager. Trying again.");
+ SysUIComponent rootComponent = SystemUIFactory.getInstance().getSysUIComponent();
+ try {
+ Method injectMethod = rootComponent.getClass()
+ .getMethod("inject", getClass());
+ injectMethod.invoke(rootComponent, this);
+ Log.w("TAG", "mMediaManager is now: " + mMediaManager);
+ } catch (NoSuchMethodException ex) {
+ Log.e(TAG, "Failed to find inject method for KeyguardSliceProvider", ex);
+ } catch (IllegalAccessException | InvocationTargetException ex) {
+ Log.e(TAG, "Failed to call inject", ex);
+ }
+ throw e;
+ }
mStatusBarStateController.addCallback(this);
mNextAlarmController.addCallback(this);
mZenModeController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 810cecc..f6571ef 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -251,10 +251,9 @@
// App icon
ImageView appIcon = mViewHolder.getAppIcon();
if (data.getAppIcon() != null) {
- appIcon.setImageDrawable(data.getAppIcon());
+ appIcon.setImageIcon(data.getAppIcon());
} else {
- Drawable iconDrawable = mContext.getDrawable(R.drawable.ic_music_note);
- appIcon.setImageDrawable(iconDrawable);
+ appIcon.setImageResource(R.drawable.ic_music_note);
}
// Song name
@@ -332,7 +331,7 @@
int actionId = ACTION_IDS[i];
final ImageButton button = mViewHolder.getAction(actionId);
MediaAction mediaAction = actionIcons.get(i);
- button.setImageDrawable(mediaAction.getDrawable());
+ button.setImageIcon(mediaAction.getIcon());
button.setContentDescription(mediaAction.getContentDescription());
Runnable action = mediaAction.getAction();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index 40a879a..0ed96ee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -33,7 +33,7 @@
/**
* Icon shown on player, close to app name.
*/
- val appIcon: Drawable?,
+ val appIcon: Icon?,
/**
* Artist name.
*/
@@ -109,7 +109,7 @@
/** State of a media action. */
data class MediaAction(
- val drawable: Drawable?,
+ val icon: Icon?,
val action: Runnable?,
val contentDescription: CharSequence?
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index cb6b22c..5d63cc7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -455,7 +455,7 @@
val app = builder.loadHeaderAppName()
// App Icon
- val smallIconDrawable: Drawable = sbn.notification.smallIcon.loadDrawable(context)
+ val smallIcon = sbn.notification.smallIcon
// Song name
var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
@@ -501,8 +501,13 @@
} else {
null
}
+ val mediaActionIcon = if (action.getIcon()?.getType() == Icon.TYPE_RESOURCE) {
+ Icon.createWithResource(packageContext, action.getIcon()!!.getResId())
+ } else {
+ action.getIcon()
+ }
val mediaAction = MediaAction(
- action.getIcon().loadDrawable(packageContext),
+ mediaActionIcon,
runnable,
action.title)
actionIcons.add(mediaAction)
@@ -518,7 +523,7 @@
val hasCheckedForResume = mediaEntries[key]?.hasCheckedForResume == true
val active = mediaEntries[key]?.active ?: true
onMediaDataLoaded(key, oldKey, MediaData(sbn.normalizedUserId, true, bgColor, app,
- smallIconDrawable, artist, song, artWorkIcon, actionIcons,
+ smallIcon, artist, song, artWorkIcon, actionIcons,
actionsToShowCollapsed, sbn.packageName, token, notif.contentIntent, null,
active, resumeAction = resumeAction, isLocalSession = isLocalSession,
notificationKey = key, hasCheckedForResume = hasCheckedForResume,
@@ -572,7 +577,7 @@
val source = ImageDecoder.createSource(context.getContentResolver(), uri)
return try {
ImageDecoder.decodeBitmap(source) {
- decoder, info, source -> decoder.isMutableRequired = true
+ decoder, info, source -> decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
}
} catch (e: IOException) {
Log.e(TAG, "Unable to load bitmap", e)
@@ -612,7 +617,7 @@
private fun getResumeMediaAction(action: Runnable): MediaAction {
return MediaAction(
- context.getDrawable(R.drawable.lb_ic_play),
+ Icon.createWithResource(context, R.drawable.lb_ic_play),
action,
context.getString(R.string.controls_media_resume)
)
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 9b6a9ea..d1630eb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -44,6 +44,8 @@
private static final String TAG = "MediaOutputAdapter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private ViewGroup mConnectedItem;
+
public MediaOutputAdapter(MediaOutputController controller) {
super(controller);
}
@@ -79,18 +81,6 @@
return mController.getMediaDevices().size();
}
- void onItemClick(MediaDevice device) {
- mController.connectDevice(device);
- device.setState(MediaDeviceState.STATE_CONNECTING);
- notifyDataSetChanged();
- }
-
- void onItemClick(int customizedItem) {
- if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
- mController.launchBluetoothPairing();
- }
- }
-
@Override
CharSequence getItemTitle(MediaDevice device) {
if (device.getDeviceType() == MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE
@@ -117,6 +107,10 @@
@Override
void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin) {
super.onBind(device, topMargin, bottomMargin);
+ final boolean currentlyConnected = isCurrentlyConnected(device);
+ if (currentlyConnected) {
+ mConnectedItem = mFrameLayout;
+ }
if (mController.isTransferring()) {
if (device.getState() == MediaDeviceState.STATE_CONNECTING
&& !mController.hasAdjustVolumeUserRestriction()) {
@@ -133,16 +127,16 @@
false /* showSeekBar*/, false /* showProgressBar */,
true /* showSubtitle */);
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
- mFrameLayout.setOnClickListener(v -> onItemClick(device));
+ mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
} else if (!mController.hasAdjustVolumeUserRestriction()
- && isCurrentConnected(device)) {
+ && currentlyConnected) {
setTwoLineLayout(device, null /* title */, true /* bFocused */,
true /* showSeekBar*/, false /* showProgressBar */,
false /* showSubtitle */);
initSeekbar(device);
} else {
setSingleLineLayout(getItemTitle(device), false /* bFocused */);
- mFrameLayout.setOnClickListener(v -> onItemClick(device));
+ mFrameLayout.setOnClickListener(v -> onItemClick(v, device));
}
}
}
@@ -160,5 +154,24 @@
mFrameLayout.setOnClickListener(v -> onItemClick(CUSTOMIZED_ITEM_PAIR_NEW));
}
}
+
+ private void onItemClick(View view, MediaDevice device) {
+ if (mController.isTransferring()) {
+ return;
+ }
+
+ playSwitchingAnim(mConnectedItem, view);
+ mController.connectDevice(device);
+ device.setState(MediaDeviceState.STATE_CONNECTING);
+ if (!isAnimating()) {
+ notifyDataSetChanged();
+ }
+ }
+
+ private void onItemClick(int customizedItem) {
+ if (customizedItem == CUSTOMIZED_ITEM_PAIR_NEW) {
+ mController.launchBluetoothPairing();
+ }
+ }
}
}
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 01dc6c4..2d3e77d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -16,6 +16,8 @@
package com.android.systemui.media.dialog;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.Typeface;
import android.text.TextUtils;
@@ -33,6 +35,7 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
/**
@@ -50,6 +53,7 @@
private boolean mIsDragging;
private int mMargin;
+ private boolean mIsAnimating;
Context mContext;
View mHolderView;
@@ -75,7 +79,7 @@
return device.getName();
}
- boolean isCurrentConnected(MediaDevice device) {
+ boolean isCurrentlyConnected(MediaDevice device) {
return TextUtils.equals(device.getId(),
mController.getCurrentConnectedMediaDevice().getId());
}
@@ -84,10 +88,17 @@
return mIsDragging;
}
+ boolean isAnimating() {
+ return mIsAnimating;
+ }
+
/**
* ViewHolder for binding device view.
*/
abstract class MediaDeviceBaseViewHolder extends RecyclerView.ViewHolder {
+
+ private static final int ANIM_DURATION = 200;
+
final FrameLayout mFrameLayout;
final TextView mTitleText;
final TextView mTwoLineTitleText;
@@ -123,17 +134,16 @@
private void setMargin(boolean topMargin, boolean bottomMargin) {
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mFrameLayout
.getLayoutParams();
- if (topMargin) {
- params.topMargin = mMargin;
- }
- if (bottomMargin) {
- params.bottomMargin = mMargin;
- }
+ params.topMargin = topMargin ? mMargin : 0;
+ params.bottomMargin = bottomMargin ? mMargin : 0;
mFrameLayout.setLayoutParams(params);
}
+
void setSingleLineLayout(CharSequence title, boolean bFocused) {
- mTitleText.setVisibility(View.VISIBLE);
mTwoLineLayout.setVisibility(View.GONE);
+ mProgressBar.setVisibility(View.GONE);
+ mTitleText.setVisibility(View.VISIBLE);
+ mTitleText.setTranslationY(0);
mTitleText.setText(title);
if (bFocused) {
mTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
@@ -146,9 +156,11 @@
boolean showSeekBar, boolean showProgressBar, boolean showSubtitle) {
mTitleText.setVisibility(View.GONE);
mTwoLineLayout.setVisibility(View.VISIBLE);
+ mSeekBar.setAlpha(1);
mSeekBar.setVisibility(showSeekBar ? View.VISIBLE : View.GONE);
mProgressBar.setVisibility(showProgressBar ? View.VISIBLE : View.GONE);
mSubTitleText.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
+ mTwoLineTitleText.setTranslationY(0);
if (device == null) {
mTwoLineTitleText.setText(title);
} else {
@@ -189,5 +201,53 @@
}
});
}
+
+ void playSwitchingAnim(@NonNull View from, @NonNull View to) {
+ final float delta = (float) (mContext.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_title_anim_y_delta));
+ final SeekBar fromSeekBar = from.requireViewById(R.id.volume_seekbar);
+ final TextView toTitleText = to.requireViewById(R.id.title);
+ if (fromSeekBar.getVisibility() != View.VISIBLE || toTitleText.getVisibility()
+ != View.VISIBLE) {
+ return;
+ }
+ mIsAnimating = true;
+ // Animation for title text
+ toTitleText.setTypeface(Typeface.create(FONT_SELECTED_TITLE, Typeface.NORMAL));
+ toTitleText.animate()
+ .setDuration(ANIM_DURATION)
+ .translationY(-delta)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ to.requireViewById(R.id.volume_indeterminate_progress).setVisibility(
+ View.VISIBLE);
+ }
+ });
+ // Animation for seek bar
+ fromSeekBar.animate()
+ .alpha(0)
+ .setDuration(ANIM_DURATION)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ final TextView fromTitleText = from.requireViewById(
+ R.id.two_line_title);
+ fromTitleText.setTypeface(Typeface.create(FONT_TITLE, Typeface.NORMAL));
+ fromTitleText.animate()
+ .setDuration(ANIM_DURATION)
+ .translationY(delta)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimating = false;
+ notifyDataSetChanged();
+ }
+ });
+ }
+ });
+ }
}
}
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 ebca8a7..3b82999 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -170,7 +170,7 @@
mHeaderSubtitle.setText(subTitle);
mHeaderTitle.setGravity(Gravity.NO_GRAVITY);
}
- if (!mAdapter.isDragging()) {
+ if (!mAdapter.isDragging() && !mAdapter.isAnimating()) {
mAdapter.notifyDataSetChanged();
}
// Show when remote media session is available
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 6d6d6cb..1538e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -207,6 +207,7 @@
private boolean mUseMLModel;
private float mMLModelThreshold;
private String mPackageName;
+ private float mMLResults;
private final GestureNavigationSettingsObserver mGestureNavigationSettingsObserver;
@@ -531,10 +532,10 @@
new long[]{(long) y},
};
- final float results = mBackGestureTfClassifierProvider.predict(featuresVector);
- if (results == -1) return -1;
+ mMLResults = mBackGestureTfClassifierProvider.predict(featuresVector);
+ if (mMLResults == -1) return -1;
- return results >= mMLModelThreshold ? 1 : 0;
+ return mMLResults >= mMLModelThreshold ? 1 : 0;
}
private boolean isWithinTouchRegion(int x, int y) {
@@ -611,7 +612,8 @@
(int) mDownPoint.x, (int) mDownPoint.y,
(int) mEndPoint.x, (int) mEndPoint.y,
mEdgeWidthLeft + mLeftInset,
- mDisplaySize.x - (mEdgeWidthRight + mRightInset));
+ mDisplaySize.x - (mEdgeWidthRight + mRightInset),
+ mUseMLModel ? mMLResults : -2);
}
private void onMotionEvent(MotionEvent ev) {
@@ -621,6 +623,7 @@
// either the bouncer is showing or the notification panel is hidden
mInputEventReceiver.setBatchingEnabled(false);
mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
+ mMLResults = 0;
mLogGesture = false;
mInRejectedExclusion = false;
mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index fb86535..98e5794 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -262,10 +262,10 @@
if (!PipUtils.hasSystemFeature(context)) {
Log.w(TAG, "Device not support PIP feature");
- return;
+ } else {
+ mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
+ displayController.addDisplayWindowListener(this);
}
- mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
- displayController.addDisplayWindowListener(this);
}
public Handler getUpdateHandler() {
@@ -325,9 +325,13 @@
return;
}
+ final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
+ if (initialConfig == null) {
+ Log.wtf(TAG, "Token not in record, this should not happen mToken=" + mToken);
+ return;
+ }
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
- final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
!= mPipBoundsHandler.getDisplayRotation();
final WindowContainerTransaction wct = new WindowContainerTransaction();
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 6ac4e4cb..d27f257 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipController.java
@@ -219,41 +219,38 @@
PipTaskOrganizer pipTaskOrganizer,
WindowManagerShellWrapper windowManagerShellWrapper
) {
- if (mInitialized) {
- return;
- }
+ if (!mInitialized) {
+ mInitialized = true;
+ mContext = context;
+ 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
+ final DisplayInfo displayInfo = new DisplayInfo();
+ context.getDisplay().getDisplayInfo(displayInfo);
+ mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
- mInitialized = true;
- mContext = context;
- 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
- final DisplayInfo displayInfo = new DisplayInfo();
- context.getDisplay().getDisplayInfo(displayInfo);
- mPipBoundsHandler.onDisplayInfoChanged(displayInfo);
+ mResizeAnimationDuration = context.getResources()
+ .getInteger(R.integer.config_pipResizeAnimationDuration);
+ mPipTaskOrganizer = pipTaskOrganizer;
+ mPipTaskOrganizer.registerPipTransitionCallback(this);
+ mActivityTaskManager = ActivityTaskManager.getService();
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL);
- mResizeAnimationDuration = context.getResources()
- .getInteger(R.integer.config_pipResizeAnimationDuration);
- mPipTaskOrganizer = pipTaskOrganizer;
- mPipTaskOrganizer.registerPipTransitionCallback(this);
- mActivityTaskManager = ActivityTaskManager.getService();
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter, UserHandle.USER_ALL);
+ // Initialize the last orientation and apply the current configuration
+ Configuration initialConfig = mContext.getResources().getConfiguration();
+ mLastOrientation = initialConfig.orientation;
+ loadConfigurationsAndApply(initialConfig);
- // Initialize the last orientation and apply the current configuration
- Configuration initialConfig = mContext.getResources().getConfiguration();
- mLastOrientation = initialConfig.orientation;
- loadConfigurationsAndApply(initialConfig);
-
- mMediaSessionManager =
- (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- mWindowManagerShellWrapper = windowManagerShellWrapper;
- try {
- mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register pinned stack listener", e);
+ mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
+ mWindowManagerShellWrapper = windowManagerShellWrapper;
+ try {
+ mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register pinned stack listener", e);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
index 52a2cec..0053fea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PageIndicator.java
@@ -2,6 +2,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -31,9 +32,6 @@
private static final long ANIMATION_DURATION = 250;
- // The size of a single dot in relation to the whole animation.
- private static final float SINGLE_SCALE = .4f;
-
private static final float MINOR_ALPHA = .42f;
private final ArrayList<Integer> mQueuedPositions = new ArrayList<>();
@@ -75,11 +73,10 @@
}
array.recycle();
- mPageIndicatorWidth =
- (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_width);
- mPageIndicatorHeight =
- (int) mContext.getResources().getDimension(R.dimen.qs_page_indicator_height);
- mPageDotWidth = (int) (mPageIndicatorWidth * SINGLE_SCALE);
+ Resources res = context.getResources();
+ mPageIndicatorWidth = res.getDimensionPixelSize(R.dimen.qs_page_indicator_width);
+ mPageIndicatorHeight = res.getDimensionPixelSize(R.dimen.qs_page_indicator_height);
+ mPageDotWidth = res.getDimensionPixelSize(R.dimen.qs_page_indicator_dot_width);
}
public void setNumPages(int numPages) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 2e258d5..8fec5a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -30,6 +30,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.media.AudioManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.AlarmClock;
@@ -64,6 +65,8 @@
import com.android.systemui.DualToneHandler;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
@@ -151,6 +154,8 @@
private Space mSpace;
private BatteryMeterView mBatteryRemainingIcon;
private RingerModeTracker mRingerModeTracker;
+ private DemoModeController mDemoModeController;
+ private DemoMode mDemoModeReceiver;
private boolean mAllIndicatorsEnabled;
private boolean mMicCameraIndicatorsEnabled;
@@ -207,7 +212,7 @@
StatusBarIconController statusBarIconController,
ActivityStarter activityStarter, PrivacyItemController privacyItemController,
CommandQueue commandQueue, RingerModeTracker ringerModeTracker,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger, DemoModeController demoModeController) {
super(context, attrs);
mAlarmController = nextAlarmController;
mZenController = zenModeController;
@@ -219,6 +224,7 @@
mCommandQueue = commandQueue;
mRingerModeTracker = ringerModeTracker;
mUiEventLogger = uiEventLogger;
+ mDemoModeController = demoModeController;
}
@Override
@@ -268,6 +274,7 @@
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
+ mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
mDateView = findViewById(R.id.date);
mSpace = findViewById(R.id.space);
@@ -526,6 +533,7 @@
updateStatusText();
});
mStatusBarIconController.addIconGroup(mIconManager);
+ mDemoModeController.addCallback(mDemoModeReceiver);
requestApplyInsets();
}
@@ -606,6 +614,7 @@
setListening(false);
mRingerModeTracker.getRingerModeInternal().removeObservers(this);
mStatusBarIconController.removeIconGroup(mIconManager);
+ mDemoModeController.removeCallback(mDemoModeReceiver);
super.onDetachedFromWindow();
}
@@ -769,4 +778,33 @@
private boolean getChipEnabled() {
return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
}
+
+ private static class ClockDemoModeReceiver implements DemoMode {
+ private Clock mClockView;
+
+ @Override
+ public List<String> demoCommands() {
+ return List.of(COMMAND_CLOCK);
+ }
+
+ ClockDemoModeReceiver(Clock clockView) {
+ mClockView = clockView;
+ }
+
+ @Override
+ public void dispatchDemoCommand(String command, Bundle args) {
+ mClockView.dispatchDemoCommand(command, args);
+ }
+
+ @Override
+ public void onDemoModeStarted() {
+ mClockView.onDemoModeStarted();
+ }
+
+ @Override
+ public void onDemoModeFinished() {
+ mClockView.onDemoModeFinished();
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 47002683..bbeff6e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -16,30 +16,17 @@
package com.android.systemui.recents;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.app.trust.TrustManager;
import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
-import android.view.Display;
-import android.widget.Toast;
import com.android.systemui.Dependency;
-import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.wm.shell.splitscreen.SplitScreen;
import java.util.Optional;
@@ -56,7 +43,6 @@
private final static String TAG = "OverviewProxyRecentsImpl";
@Nullable
private final Lazy<StatusBar> mStatusBarLazy;
- private final Optional<SplitScreen> mSplitScreenOptional;
private Context mContext;
private Handler mHandler;
@@ -65,10 +51,8 @@
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
- public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy,
- Optional<SplitScreen> splitScreenOptional) {
+ public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy) {
mStatusBarLazy = statusBarLazy.orElse(null);
- mSplitScreenOptional = splitScreenOptional;
}
@Override
@@ -140,42 +124,4 @@
// Do nothing
}
}
-
- @Override
- public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
- int metricsDockAction) {
- Point realSize = new Point();
- if (initialBounds == null) {
- mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
- .getRealSize(realSize);
- initialBounds = new Rect(0, 0, realSize.x, realSize.y);
- }
-
- ActivityManager.RunningTaskInfo runningTask =
- ActivityManagerWrapper.getInstance().getRunningTask();
- final int activityType = runningTask != null
- ? runningTask.configuration.windowConfiguration.getActivityType()
- : ACTIVITY_TYPE_UNDEFINED;
- boolean screenPinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
- boolean isRunningTaskInHomeOrRecentsStack =
- activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS;
- if (runningTask != null && !isRunningTaskInHomeOrRecentsStack && !screenPinningActive) {
- if (runningTask.supportsSplitScreenMultiWindow) {
- if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary(
- runningTask.id, stackCreateMode, initialBounds)) {
- mSplitScreenOptional.ifPresent(splitScreen -> {
- splitScreen.onDockedTopTask();
- // The overview service is handling split screen, so just skip the wait
- // for the first draw and notify the divider to start animating now
- splitScreen.onRecentsDrawn();
- });
- return true;
- }
- } else {
- Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
- Toast.LENGTH_SHORT).show();
- }
- }
- return false;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index df61fd1..6f6dd9c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -19,7 +19,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.provider.Settings;
import com.android.systemui.SystemUI;
@@ -120,17 +119,6 @@
mImpl.cancelPreloadRecentApps();
}
- public boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
- int metricsDockAction) {
- // Ensure the device has been provisioned before allowing the user to interact with
- // recents
- if (!isUserSetup()) {
- return false;
- }
-
- return mImpl.splitPrimaryTask(stackCreateMode, initialBounds, metricsDockAction);
- }
-
/**
* @return whether this device is provisioned and the current user is set up.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
index a641730..8848dbb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
@@ -17,7 +17,6 @@
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Rect;
import java.io.PrintWriter;
@@ -35,10 +34,6 @@
default void showRecentApps(boolean triggeredFromAltTab) {}
default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {}
default void toggleRecentApps() {}
- default boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
- int metricsDockAction) {
- return false;
- }
default void dump(PrintWriter pw) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index b9b4f42..6202057 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -16,9 +16,6 @@
package com.android.systemui.shortcut;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-
import android.content.Context;
import android.content.res.Configuration;
import android.os.RemoteException;
@@ -29,7 +26,6 @@
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.recents.Recents;
import com.android.wm.shell.splitscreen.DividerView;
import com.android.wm.shell.splitscreen.SplitScreen;
@@ -46,7 +42,6 @@
private static final String TAG = "ShortcutKeyDispatcher";
private final Optional<SplitScreen> mSplitScreenOptional;
- private final Recents mRecents;
private ShortcutKeyServiceProxy mShortcutKeyServiceProxy = new ShortcutKeyServiceProxy(this);
private IWindowManager mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
@@ -60,11 +55,9 @@
protected final long SC_DOCK_RIGHT = META_MASK | KeyEvent.KEYCODE_RIGHT_BRACKET;
@Inject
- public ShortcutKeyDispatcher(Context context,
- Optional<SplitScreen> splitScreenOptional, Recents recents) {
+ public ShortcutKeyDispatcher(Context context, Optional<SplitScreen> splitScreenOptional) {
super(context);
mSplitScreenOptional = splitScreenOptional;
- mRecents = recents;
}
/**
@@ -96,8 +89,7 @@
}
private void handleDockKey(long shortcutCode) {
- if (mSplitScreenOptional.isPresent()) {
- SplitScreen splitScreen = mSplitScreenOptional.get();
+ mSplitScreenOptional.ifPresent(splitScreen -> {
if (splitScreen.isDividerVisible()) {
// If there is already a docked window, we respond by resizing the docking pane.
DividerView dividerView = splitScreen.getDividerView();
@@ -112,12 +104,9 @@
dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
true /* logMetrics */);
return;
+ } else {
+ splitScreen.splitPrimaryTask();
}
- }
-
- // Split the screen
- mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
+ });
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 500de2d..9320499 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManagerKt.BUCKET_SILENT;
import static com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.ANCHOR_SCROLLING;
@@ -73,6 +74,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.keyguard.KeyguardSliceView;
import com.android.settingslib.Utils;
@@ -97,7 +99,6 @@
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
@@ -260,6 +261,7 @@
private boolean mDismissAllInProgress;
private boolean mFadeNotificationsOnDismiss;
private FooterDismissListener mFooterDismissListener;
+ private boolean mFlingAfterUpEvent;
/**
* Was the scroller scrolled to the top when the down motion was observed?
@@ -3789,6 +3791,13 @@
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
float currentOverScrollTop = getCurrentOverScrollAmount(true);
if (currentOverScrollTop == 0.0f || initialVelocity > 0) {
+ mFlingAfterUpEvent = true;
+ setFinishScrollingCallback(() -> {
+ mFlingAfterUpEvent = false;
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ setFinishScrollingCallback(null);
+ });
fling(-initialVelocity);
} else {
onOverScrollFling(false, initialVelocity);
@@ -3840,6 +3849,10 @@
return true;
}
+ boolean isFlingAfterUpEvent() {
+ return mFlingAfterUpEvent;
+ }
+
@ShadeViewRefactor(RefactorComponent.INPUT)
protected boolean isInsideQsContainer(MotionEvent ev) {
return ev.getY() < mQsContainer.getBottom();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 703c214..6820819 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -19,6 +19,7 @@
import static android.service.notification.NotificationStats.DISMISSAL_SHADE;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
@@ -47,6 +48,7 @@
import android.widget.FrameLayout;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -1556,6 +1558,14 @@
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mView.setCheckForLeaveBehind(true);
}
+
+ // When swiping directly on the NSSL, this would only get an onTouchEvent.
+ // We log any touches other than down, which will be captured by onTouchEvent.
+ // In the intercept we only start tracing when it's not a down (otherwise that down
+ // would be duplicated when intercepted).
+ if (scrollWantsIt && ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
return swipeWantsIt || scrollWantsIt || expandWantsIt;
}
@@ -1611,7 +1621,32 @@
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mView.setCheckForLeaveBehind(true);
}
+ traceJankOnTouchEvent(ev.getActionMasked(), scrollerWantsIt);
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
}
+
+ private void traceJankOnTouchEvent(int action, boolean scrollerWantsIt) {
+ // Handle interaction jank monitor cases.
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ if (scrollerWantsIt) {
+ InteractionJankMonitor.getInstance()
+ .begin(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (scrollerWantsIt && !mView.isFlingAfterUpEvent()) {
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ if (scrollerWantsIt) {
+ InteractionJankMonitor.getInstance()
+ .cancel(CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
+ }
+ break;
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 3d51854..54fb863 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -168,7 +168,7 @@
int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState);
if (!mDrawableCache.contains(iconRes)) {
- mDrawableCache.put(iconRes, getResources().getDrawable(iconRes));
+ mDrawableCache.put(iconRes, getContext().getDrawable(iconRes));
}
return mDrawableCache.get(iconRes);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index cd9cc07..3f636ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -18,6 +18,8 @@
import static android.view.View.GONE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
@@ -62,6 +64,7 @@
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
@@ -1139,6 +1142,7 @@
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mNotificationStackScrollLayoutController.cancelLongPress();
}
break;
@@ -1170,6 +1174,7 @@
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
mView.getParent().requestDisallowInterceptTouchEvent(true);
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
onQsExpansionStarted();
notifyExpandingFinished();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -1202,6 +1207,19 @@
&& x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
}
+ private void traceQsJank(boolean startTracing, boolean wasCancelled) {
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (startTracing) {
+ monitor.begin(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ } else {
+ if (wasCancelled) {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ } else {
+ monitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
+ }
+ }
+ }
+
private void initDownStates(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mOnlyAffordanceInThisMotion = false;
@@ -1315,9 +1333,9 @@
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mBarState != KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
-
// Down in the empty area while fully expanded - go to QS.
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mConflictingQsExpansionGesture = true;
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
@@ -1405,6 +1423,7 @@
return;
}
mExpectingSynthesizedDown = true;
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
onTrackingStarted();
updatePanelExpanded();
}
@@ -1474,6 +1493,7 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mQsTracking = true;
+ traceQsJank(true /* startTracing */, false /* wasCancelled */);
mInitialTouchY = y;
mInitialTouchX = x;
onQsExpansionStarted();
@@ -1513,6 +1533,9 @@
if (fraction != 0f || y >= mInitialTouchY) {
flingQsWithCurrentVelocity(y,
event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+ } else {
+ traceQsJank(false /* startTracing */,
+ event.getActionMasked() == MotionEvent.ACTION_CANCEL);
}
if (mQsVelocityTracker != null) {
mQsVelocityTracker.recycle();
@@ -1893,7 +1916,7 @@
* @see #flingSettings(float, int, Runnable, boolean)
*/
public void flingSettings(float vel, int type) {
- flingSettings(vel, type, null, false /* isClick */);
+ flingSettings(vel, type, null /* onFinishRunnable */, false /* isClick */);
}
/**
@@ -1923,6 +1946,7 @@
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
+ traceQsJank(false /* startTracing */, type != FLING_EXPAND /* wasCancelled */);
return;
}
@@ -1947,12 +1971,18 @@
setQsExpansion((Float) animation.getAnimatedValue());
});
animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mIsCanceled;
@Override
public void onAnimationStart(Animator animation) {
notifyExpandingStarted();
}
@Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCanceled = true;
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
mAnimatingQS = false;
notifyExpandingFinished();
@@ -1961,6 +1991,7 @@
if (onFinishRunnable != null) {
onFinishRunnable.run();
}
+ traceQsJank(false /* startTracing */, mIsCanceled /* wasCancelled */);
}
});
// Let's note that we're animating QS. Moving the animator here will cancel it immediately,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index bc80a1a..a4fc3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -49,6 +49,7 @@
import android.view.WindowInsetsController;
import android.widget.FrameLayout;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.FloatingToolbar;
import com.android.systemui.R;
@@ -145,6 +146,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setWillNotDraw(!DEBUG);
+ InteractionJankMonitor.getInstance().init(this);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 6fa99ba..5a01f47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
@@ -40,6 +41,7 @@
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.DejankUtils;
@@ -109,6 +111,7 @@
private boolean mMotionAborted;
private boolean mUpwardsWhenThresholdReached;
private boolean mAnimatingOnDown;
+ private boolean mHandlingPointerUp;
private ValueAnimator mHeightAnimator;
private ObjectAnimator mPeekAnimator;
@@ -356,6 +359,9 @@
protected void startExpandMotion(float newX, float newY, boolean startTracking,
float expandedHeight) {
+ if (!mHandlingPointerUp) {
+ InteractionJankMonitor.getInstance().begin(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
mInitialOffsetOnTouch = expandedHeight;
mInitialTouchY = newY;
mInitialTouchX = newX;
@@ -571,6 +577,7 @@
target = getMaxPanelHeight() - getClearAllHeightWithPadding();
}
if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
+ InteractionJankMonitor.getInstance().end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
notifyExpandingFinished();
return;
}
@@ -622,7 +629,12 @@
}
setAnimator(null);
if (!mCancelled) {
+ InteractionJankMonitor.getInstance()
+ .end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
notifyExpandingFinished();
+ } else {
+ InteractionJankMonitor.getInstance()
+ .cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
notifyBarPanelExpansionChanged();
}
@@ -1272,7 +1284,9 @@
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
+ mHandlingPointerUp = true;
startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
+ mHandlingPointerUp = false;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
@@ -1330,6 +1344,12 @@
case MotionEvent.ACTION_CANCEL:
addMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
+ InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ monitor.end(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ } else {
+ monitor.cancel(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ }
break;
}
return !mGestureWaitForTouchSlop || mTracking;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 994af09..e7c29b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
@@ -37,7 +35,6 @@
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
-import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_LEFT;
import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -174,7 +171,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
-import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -718,7 +714,6 @@
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
Optional<SplitScreen> splitScreenOptional,
@@ -799,7 +794,6 @@
mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
- mRecentsOptional = recentsOptional;
mStatusBarComponentBuilder = statusBarComponentBuilder;
mPluginManager = pluginManager;
mSplitScreenOptional = splitScreenOptional;
@@ -1548,35 +1542,37 @@
}
public boolean toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
- if (!mRecentsOptional.isPresent()) {
+ if (!mSplitScreenOptional.isPresent()) {
return false;
}
- if (mSplitScreenOptional.isPresent()) {
- SplitScreen splitScreen = mSplitScreenOptional.get();
- if (splitScreen.isDividerVisible()) {
- if (splitScreen.isMinimized()
- && !splitScreen.isHomeStackResizable()) {
- // Undocking from the minimized state is not supported
- return false;
- } else {
- splitScreen.onUndockingTask();
- if (metricsUndockAction != -1) {
- mMetricsLogger.action(metricsUndockAction);
- }
- }
- return true;
+ final SplitScreen splitScreen = mSplitScreenOptional.get();
+ if (splitScreen.isDividerVisible()) {
+ if (splitScreen.isMinimized() && !splitScreen.isHomeStackResizable()) {
+ // Undocking from the minimized state is not supported
+ return false;
}
+
+ splitScreen.onUndockingTask();
+ if (metricsUndockAction != -1) {
+ mMetricsLogger.action(metricsUndockAction);
+ }
+ return true;
}
final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
if (navbarPos == NAV_BAR_POS_INVALID) {
return false;
}
- int createMode = navbarPos == NAV_BAR_POS_LEFT
- ? SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
- : SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
- return mRecentsOptional.get().splitPrimaryTask(createMode, null, metricsDockAction);
+
+ if (splitScreen.splitPrimaryTask()) {
+ if (metricsDockAction != -1) {
+ mMetricsLogger.action(metricsDockAction);
+ }
+ return true;
+ }
+
+ return false;
}
/**
@@ -4121,8 +4117,6 @@
protected Display mDisplay;
private int mDisplayId;
- private final Optional<Recents> mRecentsOptional;
-
protected NotificationShelfController mNotificationShelfController;
private final Lazy<AssistManager> mAssistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 3f29a4e..6d4099b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -43,7 +43,6 @@
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.PluginDependencyProvider;
-import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
@@ -175,7 +174,6 @@
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- Optional<Recents> recentsOptional,
Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
PluginManager pluginManager,
Optional<SplitScreen> splitScreenOptional,
@@ -254,7 +252,6 @@
dozeScrimController,
volumeComponent,
commandQueue,
- recentsOptional,
statusBarComponentBuilder,
pluginManager,
splitScreenOptional,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
index 7a78c15..0bd3624 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
@@ -58,6 +58,10 @@
if (!mNotificationHandlerPackage.isEmpty()) {
startNotificationHandlerActivity(
new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
+ } else {
+ Log.w(TAG,
+ "Not toggling notification panel: config_notificationHandlerPackage is "
+ + "empty");
}
}
@@ -66,6 +70,10 @@
if (!mNotificationHandlerPackage.isEmpty()) {
startNotificationHandlerActivity(
new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
+ } else {
+ Log.w(TAG,
+ "Not expanding notification panel: config_notificationHandlerPackage is "
+ + "empty");
}
}
@@ -77,6 +85,9 @@
NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
closeNotificationIntent.setPackage(mNotificationHandlerPackage);
mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
+ } else {
+ Log.w(TAG,
+ "Not closing notification panel: config_notificationHandlerPackage is empty");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
index a50de45..1e6a9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java
@@ -32,6 +32,8 @@
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
+import java.util.concurrent.Executor;
+
import dagger.Module;
import dagger.Provides;
@@ -45,9 +47,10 @@
@SysUISingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @Main Handler mainHandler,
+ DisplayController displayController, @Main Executor mainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
+ return new DisplayImeController(wmService, displayController, mainExecutor,
+ transactionPool);
}
static SplitScreen provideSplitScreen(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 3142c1e..b4852b2 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -45,6 +45,7 @@
import com.android.wm.shell.splitscreen.SplitScreenController;
import java.util.Optional;
+import java.util.concurrent.Executor;
import dagger.Module;
import dagger.Provides;
@@ -59,9 +60,10 @@
@SysUISingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
- DisplayController displayController, @Main Handler mainHandler,
+ DisplayController displayController, @Main Executor mainExecutor,
TransactionPool transactionPool) {
- return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
+ return new DisplayImeController(wmService, displayController, mainExecutor,
+ transactionPool);
}
@SysUISingleton
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 539b321..c6440f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -17,10 +17,17 @@
package com.android.systemui.accessibility;
import static android.view.Choreographer.FrameCallback;
+import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@@ -37,17 +44,20 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -71,6 +81,7 @@
private Resources mResources;
private WindowMagnificationController mWindowMagnificationController;
private Instrumentation mInstrumentation;
+ private View mMirrorView;
@Before
public void setUp() {
@@ -83,12 +94,16 @@
).when(mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
doAnswer(invocation -> {
- View view = invocation.getArgument(0);
+ mMirrorView = invocation.getArgument(0);
WindowManager.LayoutParams lp = invocation.getArgument(1);
- view.setLayoutParams(lp);
+ mMirrorView.setLayoutParams(lp);
return null;
}).when(mWindowManager).addView(any(View.class), any(WindowManager.LayoutParams.class));
doAnswer(invocation -> {
+ mMirrorView = null;
+ return null;
+ }).when(mWindowManager).removeView(any(View.class));
+ doAnswer(invocation -> {
FrameCallback callback = invocation.getArgument(0);
callback.doFrame(0);
return null;
@@ -147,14 +162,18 @@
}
@Test
- public void setScale_enabled_expectedValue() {
+ public void setScale_enabled_expectedValueAndUpdateStateDescription() {
mInstrumentation.runOnMainSync(
- () -> mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+ () -> mWindowMagnificationController.enableWindowMagnification(2.0f, Float.NaN,
Float.NaN));
mInstrumentation.runOnMainSync(() -> mWindowMagnificationController.setScale(3.0f));
assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
+ ArgumentCaptor<Runnable> runnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mHandler).postDelayed(runnableArgumentCaptor.capture(), anyLong());
+ runnableArgumentCaptor.getValue().run();
+ assertThat(mMirrorView.getStateDescription().toString(), containsString("300"));
}
@Test
@@ -227,4 +246,52 @@
verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt());
}
+
+ @Test
+ public void initializeA11yNode_enabled_expectedValues() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
+ Float.NaN);
+ });
+ assertNotNull(mMirrorView);
+ final AccessibilityNodeInfo nodeInfo = new AccessibilityNodeInfo();
+
+ mMirrorView.onInitializeAccessibilityNodeInfo(nodeInfo);
+
+ assertNotNull(nodeInfo.getContentDescription());
+ assertThat(nodeInfo.getStateDescription().toString(), containsString("250"));
+ assertThat(nodeInfo.getActionList(),
+ hasItems(new AccessibilityAction(R.id.accessibility_action_zoom_in, null),
+ new AccessibilityAction(R.id.accessibility_action_zoom_out, null),
+ new AccessibilityAction(R.id.accessibility_action_move_right, null),
+ new AccessibilityAction(R.id.accessibility_action_move_left, null),
+ new AccessibilityAction(R.id.accessibility_action_move_down, null),
+ new AccessibilityAction(R.id.accessibility_action_move_up, null)));
+ }
+
+ @Test
+ public void performA11yActions_visible_expectedResults() {
+ mInstrumentation.runOnMainSync(() -> {
+ mWindowMagnificationController.enableWindowMagnification(2.5f, Float.NaN,
+ Float.NaN);
+ });
+ assertNotNull(mMirrorView);
+
+ assertTrue(
+ mMirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_out, null));
+ // Minimum scale is 2.0.
+ assertEquals(2.0f, mWindowMagnificationController.getScale(), 0f);
+
+ assertTrue(mMirrorView.performAccessibilityAction(R.id.accessibility_action_zoom_in, null));
+ assertEquals(3.0f, mWindowMagnificationController.getScale(), 0f);
+
+ // TODO: Verify the final state when the mirror surface is visible.
+ assertTrue(mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_up, null));
+ assertTrue(
+ mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_down, null));
+ assertTrue(
+ mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_right, null));
+ assertTrue(
+ mMirrorView.performAccessibilityAction(R.id.accessibility_action_move_left, null));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 9b9f840..7df9f1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.biometrics;
+import static junit.framework.Assert.assertEquals;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.eq;
@@ -25,7 +27,9 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -54,11 +58,18 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class UdfpsControllerTest extends SysuiTestCase {
+ // Use this for inputs going into SystemUI. Use UdfpsController.mUdfpsSensorId for things
+ // leaving SystemUI.
+ private static final int TEST_UDFPS_SENSOR_ID = 1;
+
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@@ -98,6 +109,13 @@
public void setUp() {
setUpResources();
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false)).thenReturn(mUdfpsView);
+ final List<FingerprintSensorProperties> props = new ArrayList<>();
+ props.add(new FingerprintSensorProperties(TEST_UDFPS_SENSOR_ID,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
+ true /* resetLockoutRequiresHardwareAuthToken */));
+ when(mFingerprintManager.getSensorProperties()).thenReturn(props);
mSystemSettings = new FakeSettings();
mFgExecutor = new FakeExecutor(new FakeSystemClock());
mUdfpsController = new UdfpsController(
@@ -112,6 +130,8 @@
mFgExecutor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
+
+ assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mUdfpsSensorId);
}
private void setUpResources() {
@@ -138,15 +158,15 @@
@Test
public void showUdfpsOverlay_addsViewToWindow() throws RemoteException {
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
verify(mWindowManager).addView(eq(mUdfpsView), any());
}
@Test
public void hideUdfpsOverlay_removesViewFromWindow() throws RemoteException {
- mOverlayController.showUdfpsOverlay();
- mOverlayController.hideUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
+ mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
verify(mWindowManager).removeView(eq(mUdfpsView));
}
@@ -156,7 +176,7 @@
// GIVEN that the bouncer is showing
mUdfpsController.setBouncerVisibility(/* isShowing */ true);
// WHEN a request to show the overlay is received
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
// THEN the overlay is not attached
verify(mWindowManager, never()).addView(eq(mUdfpsView), any());
@@ -165,7 +185,7 @@
@Test
public void setBouncerVisibility_overlayDetached() throws RemoteException {
// GIVEN that the overlay has been requested
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
// WHEN the bouncer becomes visible
mUdfpsController.setBouncerVisibility(/* isShowing */ true);
mFgExecutor.runAllReady();
@@ -178,7 +198,7 @@
// GIVEN that the bouncer is visible
mUdfpsController.setBouncerVisibility(/* isShowing */ true);
// AND the overlay has been requested
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
// WHEN the bouncer is closed
mUdfpsController.setBouncerVisibility(/* isShowing */ false);
mFgExecutor.runAllReady();
@@ -193,7 +213,7 @@
when(mUdfpsView.isValidTouch(anyFloat(), anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
// WHEN ACTION_DOWN is received
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
@@ -201,7 +221,8 @@
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
event.recycle();
// THEN the event is passed to the FingerprintManager
- verify(mFingerprintManager).onFingerDown(eq(0), eq(0), eq(0f), eq(0f));
+ verify(mFingerprintManager).onFingerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
+ eq(0f), eq(0f));
// AND the scrim and dot is shown
verify(mUdfpsView).showScrimAndDot();
}
@@ -209,12 +230,13 @@
@Test
public void aodInterrupt() throws RemoteException {
// GIVEN that the overlay is showing
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0);
// THEN the event is passed to the FingerprintManager
- verify(mFingerprintManager).onFingerDown(eq(0), eq(0), anyFloat(), anyFloat());
+ verify(mFingerprintManager).onFingerDown(eq(mUdfpsController.mUdfpsSensorId), eq(0), eq(0),
+ anyFloat(), anyFloat());
// AND the scrim and dot is shown
verify(mUdfpsView).showScrimAndDot();
}
@@ -222,7 +244,7 @@
@Test
public void cancelAodInterrupt() throws RemoteException {
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0);
// WHEN it is cancelled
@@ -234,7 +256,7 @@
@Test
public void aodInterruptTimeout() throws RemoteException {
// GIVEN AOD interrupt
- mOverlayController.showUdfpsOverlay();
+ mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
mFgExecutor.runAllReady();
mUdfpsController.onAodInterrupt(0, 0);
// WHEN it times out
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 0e376bd..2d460aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -192,6 +192,7 @@
public void onItemClick_clickDevice_verifyConnectDevice() {
assertThat(mMediaDevice2.getState()).isEqualTo(
LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
mViewHolder.mFrameLayout.performClick();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 23b12d4..a6ea9966a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -93,7 +93,6 @@
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.Recents;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
@@ -232,7 +231,6 @@
@Mock private KeyguardLiftController mKeyguardLiftController;
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
- @Mock private Recents mRecents;
@Mock private Provider<StatusBarComponent.Builder> mStatusBarComponentBuilderProvider;
@Mock private StatusBarComponent.Builder mStatusBarComponentBuilder;
@Mock private StatusBarComponent mStatusBarComponent;
@@ -392,7 +390,6 @@
mDozeScrimController,
mVolumeComponent,
mCommandQueue,
- Optional.of(mRecents),
mStatusBarComponentBuilderProvider,
mPluginManager,
Optional.of(mSplitScreen),
diff --git a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
index 73fc833..084743d 100644
--- a/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
+++ b/packages/Tethering/src/android/net/ip/NeighborPacketForwarder.java
@@ -25,7 +25,6 @@
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;
@@ -33,6 +32,8 @@
import android.system.Os;
import android.util.Log;
+import com.android.net.module.util.PacketReader;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.Inet6Address;
diff --git a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 7dd5290..64d5025 100644
--- a/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -2104,7 +2104,7 @@
}
private boolean hasCallingPermission(@NonNull String permission) {
- return mContext.checkCallingPermission(permission) == PERMISSION_GRANTED;
+ return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
/** Unregister tethering event callback */
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 64be2d9..d206ea0 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -553,7 +553,6 @@
TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
TestNetworkInterface iface = tnm.createTapInterface();
Log.d(TAG, "Created test interface " + iface.getInterfaceName());
- assertNotNull(NetworkInterface.getByName(iface.getInterfaceName()));
return iface;
}
diff --git a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
index 2465fe0..e7eeb30 100644
--- a/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
+++ b/packages/overlays/IconShapePebbleOverlay/res/values/config.xml
@@ -18,7 +18,7 @@
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Specifies the path that is used by AdaptiveIconDrawable class to crop launcher icons. -->
- <string name="config_icon_mask" translatable="false">"MM55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z"</string>
+ <string name="config_icon_mask" translatable="false">"M55,0 C25,0 0,25 0,50 0,78 28,100 55,100 85,100 100,85 100,58 100,30 86,0 55,0 Z"</string>
<!-- Flag indicating whether round icons should be parsed from the application manifest. -->
<bool name="config_useRoundIcon">false</bool>
<!-- Corner radius of system dialogs -->
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 80e9703..ae33f0c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -119,8 +119,8 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
+import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.MagnificationGestureHandler;
-import com.android.server.accessibility.magnification.MagnificationTransitionController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -219,16 +219,12 @@
// Lazily initialized - access through getSystemActionPerfomer()
private SystemActionPerformer mSystemActionPerformer;
- private FullScreenMagnificationController mFullScreenMagnificationController;
-
private InteractionBridge mInteractionBridge;
private AlertDialog mEnableTouchExplorationDialog;
private AccessibilityInputFilter mInputFilter;
- private WindowMagnificationManager mWindowMagnificationMgr;
-
private boolean mHasInputFilter;
private KeyEventDispatcher mKeyEventDispatcher;
@@ -259,7 +255,7 @@
private Point mTempPoint = new Point();
private boolean mIsAccessibilityButtonShown;
- private MagnificationTransitionController mMagnificationTransitionController;
+ private MagnificationController mMagnificationController;
private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
@@ -292,7 +288,7 @@
SystemActionPerformer systemActionPerformer,
AccessibilityWindowManager a11yWindowManager,
AccessibilityDisplayListener a11yDisplayListener,
- WindowMagnificationManager windowMagnificationMgr) {
+ MagnificationController magnificationController) {
mContext = context;
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
@@ -303,8 +299,7 @@
mSystemActionPerformer = systemActionPerformer;
mA11yWindowManager = a11yWindowManager;
mA11yDisplayListener = a11yDisplayListener;
- mWindowMagnificationMgr = windowMagnificationMgr;
- mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
+ mMagnificationController = magnificationController;
init();
}
@@ -324,7 +319,7 @@
mA11yWindowManager = new AccessibilityWindowManager(mLock, mMainHandler,
mWindowManagerService, this, mSecurityPolicy, this);
mA11yDisplayListener = new AccessibilityDisplayListener(mContext, mMainHandler);
- mMagnificationTransitionController = new MagnificationTransitionController(this, mLock);
+ mMagnificationController = new MagnificationController(this, mLock, mContext);
init();
}
@@ -1195,9 +1190,7 @@
// The user changed.
mCurrentUserId = userId;
- if (mWindowMagnificationMgr != null) {
- mWindowMagnificationMgr.setUserId(mCurrentUserId);
- }
+ mMagnificationController.updateUserIdIfNeeded(mCurrentUserId);
AccessibilityUserState userState = getCurrentUserStateLocked();
readConfigurationForUserStateLocked(userState);
@@ -1554,7 +1547,7 @@
if (fallBackMagnificationModeSettingsLocked(userState)) {
return;
}
- mMagnificationTransitionController.transitionMagnificationModeLocked(
+ mMagnificationController.transitionMagnificationModeLocked(
Display.DEFAULT_DISPLAY, userState.getMagnificationModeLocked(),
this::onMagnificationTransitionEndedLocked);
}
@@ -2310,13 +2303,9 @@
return;
}
- if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.setUserId(userState.mUserId);
- }
-
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()
- && mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.unregisterAll();
+ && mMagnificationController.isFullScreenMagnificationControllerInitialized()) {
+ getFullScreenMagnificationController().unregisterAll();
return;
}
@@ -2339,8 +2328,8 @@
final int displayId = display.getDisplayId();
if (userHasListeningMagnificationServicesLocked(userState, displayId)) {
getFullScreenMagnificationController().register(displayId);
- } else if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.unregister(displayId);
+ } else if (mMagnificationController.isFullScreenMagnificationControllerInitialized()) {
+ getFullScreenMagnificationController().unregister(displayId);
}
}
}
@@ -2990,10 +2979,7 @@
*/
public WindowMagnificationManager getWindowMagnificationMgr() {
synchronized (mLock) {
- if (mWindowMagnificationMgr == null) {
- mWindowMagnificationMgr = new WindowMagnificationManager(mContext, mCurrentUserId);
- }
- return mWindowMagnificationMgr;
+ return mMagnificationController.getWindowMagnificationMgr();
}
}
@@ -3060,12 +3046,7 @@
@Override
public FullScreenMagnificationController getFullScreenMagnificationController() {
synchronized (mLock) {
- if (mFullScreenMagnificationController == null) {
- mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
- this, mLock);
- mFullScreenMagnificationController.setUserId(mCurrentUserId);
- }
- return mFullScreenMagnificationController;
+ return mMagnificationController.getFullScreenMagnificationController();
}
}
@@ -3307,9 +3288,7 @@
}
}
}
- if (mFullScreenMagnificationController != null) {
- mFullScreenMagnificationController.onDisplayRemoved(displayId);
- }
+ mMagnificationController.onDisplayRemoved(displayId);
mA11yWindowManager.stopTrackingWindows(displayId);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
similarity index 66%
rename from services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
rename to services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index af4b34f..0dc5267 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationTransitionController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
@@ -29,21 +30,26 @@
import android.util.SparseArray;
import android.view.accessibility.MagnificationAnimationCallback;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.accessibility.AccessibilityManagerService;
/**
- * Handles magnification mode transition.
+ * Handles all magnification controllers initialization, generic interactions
+ * and magnification mode transition.
*/
-public class MagnificationTransitionController {
+public class MagnificationController {
private static final boolean DEBUG = false;
private static final String TAG = "MagnificationController";
private final AccessibilityManagerService mAms;
private final PointF mTempPoint = new PointF();
private final Object mLock;
+ private final Context mContext;
private final SparseArray<DisableMagnificationCallback>
mMagnificationEndRunnableSparseArray = new SparseArray();
+ private FullScreenMagnificationController mFullScreenMagnificationController;
+ private WindowMagnificationManager mWindowMagnificationMgr;
/**
* A callback to inform the magnification transition result.
@@ -56,9 +62,20 @@
void onResult(boolean success);
}
- public MagnificationTransitionController(AccessibilityManagerService ams, Object lock) {
+ public MagnificationController(AccessibilityManagerService ams, Object lock,
+ Context context) {
mAms = ams;
mLock = lock;
+ mContext = context;
+ }
+
+ @VisibleForTesting
+ public MagnificationController(AccessibilityManagerService ams, Object lock,
+ Context context, FullScreenMagnificationController fullScreenMagnificationController,
+ WindowMagnificationManager windowMagnificationManager) {
+ this(ams, lock, context);
+ mFullScreenMagnificationController = fullScreenMagnificationController;
+ mWindowMagnificationMgr = windowMagnificationManager;
}
/**
@@ -97,7 +114,7 @@
}
final FullScreenMagnificationController screenMagnificationController =
getFullScreenMagnificationController();
- final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationManager();
+ final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr();
final float scale = windowMagnificationMgr.getPersistedScale();
final DisableMagnificationCallback animationEndCallback =
new DisableMagnificationCallback(transitionCallBack, displayId, targetMode,
@@ -111,6 +128,39 @@
setDisableMagnificationCallbackLocked(displayId, animationEndCallback);
}
+ /**
+ * Updates the active user ID of {@link FullScreenMagnificationController} and {@link
+ * WindowMagnificationManager}.
+ *
+ * @param userId the currently active user ID
+ */
+ public void updateUserIdIfNeeded(int userId) {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.setUserId(userId);
+ }
+ if (mWindowMagnificationMgr != null) {
+ mWindowMagnificationMgr.setUserId(userId);
+ }
+ }
+ }
+
+ /**
+ * Removes the magnification instance with given id.
+ *
+ * @param displayId The logical display id.
+ */
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController != null) {
+ mFullScreenMagnificationController.onDisplayRemoved(displayId);
+ }
+ if (mWindowMagnificationMgr != null) {
+ mWindowMagnificationMgr.onDisplayRemoved(displayId);
+ }
+ }
+ }
+
private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
int displayId) {
return mMagnificationEndRunnableSparseArray.get(displayId);
@@ -125,31 +175,63 @@
}
}
- private FullScreenMagnificationController getFullScreenMagnificationController() {
- return mAms.getFullScreenMagnificationController();
+ /**
+ * Getter of {@link FullScreenMagnificationController}.
+ *
+ * @return {@link FullScreenMagnificationController}.
+ */
+ public FullScreenMagnificationController getFullScreenMagnificationController() {
+ synchronized (mLock) {
+ if (mFullScreenMagnificationController == null) {
+ mFullScreenMagnificationController = new FullScreenMagnificationController(mContext,
+ mAms, mLock);
+ mFullScreenMagnificationController.setUserId(mAms.getCurrentUserIdLocked());
+ }
+ }
+ return mFullScreenMagnificationController;
}
- private WindowMagnificationManager getWindowMagnificationManager() {
- return mAms.getWindowMagnificationMgr();
+ /**
+ * Is {@link #mFullScreenMagnificationController} is initialized.
+ * @return {code true} if {@link #mFullScreenMagnificationController} is initialized.
+ */
+ public boolean isFullScreenMagnificationControllerInitialized() {
+ synchronized (mLock) {
+ return mFullScreenMagnificationController != null;
+ }
+ }
+
+ /**
+ * Getter of {@link WindowMagnificationManager}.
+ *
+ * @return {@link WindowMagnificationManager}.
+ */
+ public WindowMagnificationManager getWindowMagnificationMgr() {
+ synchronized (mLock) {
+ if (mWindowMagnificationMgr == null) {
+ mWindowMagnificationMgr = new WindowMagnificationManager(mContext,
+ mAms.getCurrentUserIdLocked());
+ }
+ return mWindowMagnificationMgr;
+ }
}
private @Nullable
PointF getCurrentMagnificationBoundsCenterLocked(int displayId, int targetMode) {
if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) {
- final WindowMagnificationManager magnificationManager = getWindowMagnificationManager();
- if (!magnificationManager.isWindowMagnifierEnabled(displayId)) {
+ if (mWindowMagnificationMgr == null
+ || !mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId)) {
return null;
}
- mTempPoint.set(magnificationManager.getCenterX(displayId),
- magnificationManager.getCenterY(displayId));
+ mTempPoint.set(mWindowMagnificationMgr.getCenterX(displayId),
+ mWindowMagnificationMgr.getCenterY(displayId));
} else {
- final FullScreenMagnificationController screenMagnificationController =
- getFullScreenMagnificationController();
- if (!screenMagnificationController.isMagnifying(displayId)) {
+ if (mFullScreenMagnificationController == null
+ || !mFullScreenMagnificationController.isMagnifying(displayId)) {
return null;
}
- mTempPoint.set(screenMagnificationController.getCenterX(displayId),
- screenMagnificationController.getCenterY(displayId));
+ mTempPoint.set(mFullScreenMagnificationController.getCenterX(displayId),
+ mFullScreenMagnificationController.getCenterY(displayId));
}
return mTempPoint;
}
@@ -228,7 +310,7 @@
mCurrentCenter.y, true,
AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
} else {
- getWindowMagnificationManager().enableWindowMagnification(mDisplayId,
+ getWindowMagnificationMgr().enableWindowMagnification(mDisplayId,
mCurrentScale, mCurrentCenter.x,
mCurrentCenter.y);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index c8e485f..ed3085f 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -452,6 +452,15 @@
return magnifier;
}
+ /**
+ * Removes the window magnifier with given id.
+ *
+ * @param displayId The logical display id.
+ */
+ void onDisplayRemoved(int displayId) {
+ disableWindowMagnification(displayId, true);
+ }
+
private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements
IBinder.DeathRecipient {
private boolean mExpiredDeathRecipient = false;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8a1baf2..da5d1c2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -136,8 +136,6 @@
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.shared.PrivateDnsConfig;
-import android.net.util.LinkPropertiesUtils.CompareOrUpdateResult;
-import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
import android.os.Binder;
@@ -195,6 +193,8 @@
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.DataConnectionStats;
diff --git a/services/core/java/com/android/server/NsdService.java b/services/core/java/com/android/server/NsdService.java
index 4a1820a..d907505 100644
--- a/services/core/java/com/android/server/NsdService.java
+++ b/services/core/java/com/android/server/NsdService.java
@@ -25,7 +25,6 @@
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
-import android.net.util.nsd.DnsSdTxtRecord;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
@@ -42,6 +41,7 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.net.module.util.DnsSdTxtRecord;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 34e6370..b874684 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -17,13 +17,13 @@
package com.android.server;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.util.ArrayMap;
import android.util.Slog;
@@ -84,6 +84,13 @@
@GuardedBy("mTargetUsers")
private final SparseArray<TargetUser> mTargetUsers = new SparseArray<>();
+ /**
+ * Reference to the current user, it's used to set the {@link TargetUser} on
+ * {@link #switchUser(int, int)} as the previous user might have been removed already.
+ */
+ @GuardedBy("mTargetUsers")
+ private @Nullable TargetUser mCurrentUser;
+
SystemServiceManager(Context context) {
mContext = context;
}
@@ -259,18 +266,22 @@
return targetUser;
}
+ private @NonNull TargetUser newTargetUser(@UserIdInt int userId) {
+ final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
+ Preconditions.checkState(userInfo != null, "No UserInfo for " + userId);
+ return new TargetUser(userInfo);
+ }
+
/**
* Starts the given user.
*/
public void startUser(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) {
- // Create cached TargetUser
- final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
- Preconditions.checkState(userInfo != null, "No UserInfo for " + userId);
+ final TargetUser targetUser = newTargetUser(userId);
synchronized (mTargetUsers) {
- mTargetUsers.put(userId, new TargetUser(userInfo));
+ mTargetUsers.put(userId, targetUser);
}
- onUser(t, START, userId);
+ onUser(t, START, /* prevUser= */ null, targetUser);
}
/**
@@ -291,7 +302,26 @@
* Switches to the given user.
*/
public void switchUser(@UserIdInt int from, @UserIdInt int to) {
- onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from);
+ final TargetUser curUser, prevUser;
+ synchronized (mTargetUsers) {
+ if (mCurrentUser == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "First user switch: from " + from + " to " + to);
+ }
+ prevUser = newTargetUser(from);
+ } else {
+ if (from != mCurrentUser.getUserIdentifier()) {
+ Slog.wtf(TAG, "switchUser(" + from + "," + to + "): mCurrentUser is "
+ + mCurrentUser + ", it should be " + from);
+ }
+ prevUser = mCurrentUser;
+ }
+ curUser = mCurrentUser = getTargetUser(to);
+ if (DEBUG) {
+ Slog.d(TAG, "Set mCurrentUser to " + mCurrentUser);
+ }
+ }
+ onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, prevUser, curUser);
}
/**
@@ -314,21 +344,16 @@
}
private void onUser(@NonNull String onWhat, @UserIdInt int userId) {
- onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userId);
+ onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, /* prevUser= */ null,
+ getTargetUser(userId));
}
private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
- @UserIdInt int userId) {
- onUser(t, onWhat, userId, UserHandle.USER_NULL);
- }
-
- private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
- @UserIdInt int curUserId, @UserIdInt int prevUserId) {
+ @Nullable TargetUser prevUser, @NonNull TargetUser curUser) {
+ final int curUserId = curUser.getUserIdentifier();
t.traceBegin("ssm." + onWhat + "User-" + curUserId);
- Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
- final TargetUser curUser = getTargetUser(curUserId);
- final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null
- : getTargetUser(prevUserId);
+ Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId
+ + (prevUser != null ? " (from " + prevUser + ")" : ""));
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
@@ -466,14 +491,16 @@
.append(service.getClass().getSimpleName())
.append("\n");
}
-
- builder.append("Target users: ");
- final int targetUsersSize = mTargetUsers.size();
- for (int i = 0; i < targetUsersSize; i++) {
- mTargetUsers.valueAt(i).dump(builder);
- if (i != targetUsersSize - 1) builder.append(',');
+ synchronized (mTargetUsers) {
+ builder.append("Current user: ").append(mCurrentUser).append('\n');
+ builder.append("Target users: ");
+ final int targetUsersSize = mTargetUsers.size();
+ for (int i = 0; i < targetUsersSize; i++) {
+ mTargetUsers.valueAt(i).dump(builder);
+ if (i != targetUsersSize - 1) builder.append(',');
+ }
+ builder.append('\n');
}
- builder.append('\n');
Slog.e(TAG, builder.toString());
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f056df..ffdcd15 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17334,4 +17334,18 @@
throw new SecurityException("Caller uid " + callerUid + " cannot set freezer state ");
}
}
+
+ /**
+ * Holds the AM lock for the specified amount of milliseconds.
+ * Intended for use by the tests that need to imitate lock contention.
+ * Requires permission identity of the shell UID.
+ */
+ @Override
+ public void holdLock(int durationMs) {
+ enforceCallingPermission(Manifest.permission.INJECT_EVENTS, "holdLock");
+
+ synchronized (this) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 1bf62a0..58ac2dc 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -733,10 +733,6 @@
uidRec.reset();
}
- if (mService.mAtmInternal != null) {
- mService.mAtmInternal.rankTaskLayersIfNeeded();
- }
-
mAdjSeq++;
if (fullUpdate) {
mNewNumServiceProcs = 0;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 2dced8d..ebc5b59 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1909,8 +1909,8 @@
}
callback.initialize(this, adj, foregroundActivities, procState, schedGroup, appUid, logUid,
processCurTop);
- final int minLayer = getWindowProcessController().computeOomAdjFromActivities(
- ProcessList.VISIBLE_APP_LAYER_MAX, callback);
+ final int minLayer = Math.min(ProcessList.VISIBLE_APP_LAYER_MAX,
+ getWindowProcessController().computeOomAdjFromActivities(callback));
mCachedAdj = callback.adj;
mCachedForegroundActivities = callback.foregroundActivities;
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 2903b9970..cc94079 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
@@ -46,6 +46,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.EventLog;
+import android.util.Pair;
import android.util.Slog;
import android.view.Surface;
@@ -79,7 +80,7 @@
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
private final LockPatternUtils mLockPatternUtils;
- private Fingerprint21 mFingerprint21;
+ @NonNull private List<ServiceProvider> mServiceProviders;
/**
* Receives the incoming binder calls from FingerprintManager.
@@ -88,11 +89,8 @@
@Override // Binder call
public List<FingerprintSensorProperties> getSensorProperties(String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- final List<FingerprintSensorProperties> properties = new ArrayList<>();
-
- if (mFingerprint21 != null) {
- properties.add(mFingerprint21.getFingerprintSensorProperties());
- }
+ final List<FingerprintSensorProperties> properties =
+ FingerprintService.this.getSensorProperties();
Slog.d(TAG, "Retrieved sensor properties for: " + opPackageName
+ ", sensors: " + properties.size());
@@ -104,18 +102,26 @@
IFingerprintServiceReceiver receiver, String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) {
- mFingerprint21.scheduleGenerateChallenge(token, receiver, opPackageName);
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
return;
}
- Slog.w(TAG, "No matching sensor for generateChallenge, sensorId: " + sensorId);
+ provider.scheduleGenerateChallenge(sensorId, token, receiver, opPackageName);
}
@Override // Binder call
- public void revokeChallenge(IBinder token, String owner) {
+ public void revokeChallenge(IBinder token, String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.scheduleRevokeChallenge(token, owner);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for revokeChallenge");
+ return;
+ }
+
+ provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName);
}
@Override // Binder call
@@ -123,14 +129,28 @@
final IFingerprintServiceReceiver receiver, final String opPackageName,
Surface surface) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.scheduleEnroll(token, hardwareAuthToken, userId, receiver, opPackageName,
- surface);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for enroll");
+ return;
+ }
+
+ provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
+ receiver, opPackageName, surface);
}
@Override // Binder call
public void cancelEnrollment(final IBinder token) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.cancelEnrollment(token);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for cancelEnrollment");
+ return;
+ }
+
+ provider.second.cancelEnrollment(provider.first, token);
}
@Override // Binder call
@@ -169,8 +189,15 @@
!= PackageManager.PERMISSION_GRANTED;
final int statsClient = isKeyguard ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
- mFingerprint21.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
- new ClientMonitorCallbackConverter(receiver), opPackageName,
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for authenticate");
+ return;
+ }
+
+ provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
+ 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
restricted, statsClient, isKeyguard);
}
@@ -191,7 +218,13 @@
return;
}
- mFingerprint21.scheduleFingerDetect(token, userId,
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for detectFingerprint");
+ return;
+ }
+
+ provider.second.scheduleFingerDetect(provider.first, token, userId,
new ClientMonitorCallbackConverter(receiver), opPackageName, surface,
BiometricsProtoEnums.CLIENT_KEYGUARD);
}
@@ -203,8 +236,14 @@
Surface surface) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for prepareForAuthentication");
+ return;
+ }
+
final boolean restricted = true; // BiometricPrompt is always restricted
- mFingerprint21.scheduleAuthenticate(token, operationId, userId, cookie,
+ provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, cookie,
new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, false /* isKeyguard */);
}
@@ -212,7 +251,14 @@
@Override // Binder call
public void startPreparedClient(int cookie) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
- mFingerprint21.startPreparedClient(cookie);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for startPreparedClient");
+ return;
+ }
+
+ provider.second.startPreparedClient(provider.first, cookie);
}
@@ -228,7 +274,13 @@
return;
}
- mFingerprint21.cancelAuthentication(token);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for cancelAuthentication");
+ return;
+ }
+
+ provider.second.cancelAuthentication(provider.first, token);
}
@Override // Binder call
@@ -242,21 +294,41 @@
// For IBiometricsFingerprint2.1, cancelling fingerprint detect is the same as
// cancelling authentication.
- mFingerprint21.cancelAuthentication(token);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for cancelFingerprintDetect");
+ return;
+ }
+
+ provider.second.cancelAuthentication(provider.first, token);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
int callingUid, int callingPid, int callingUserId) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
- mFingerprint21.cancelAuthentication(token);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for cancelAuthenticationFromService");
+ return;
+ }
+
+ provider.second.cancelAuthentication(provider.first, token);
}
@Override // Binder call
public void remove(final IBinder token, final int fingerId, final int userId,
final IFingerprintServiceReceiver receiver, final String opPackageName) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mFingerprint21.scheduleRemove(token, receiver, fingerId, userId, opPackageName);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for remove");
+ return;
+ }
+ provider.second.scheduleRemove(provider.first, token, receiver, fingerId, userId,
+ opPackageName);
}
@Override
@@ -274,10 +346,14 @@
final long ident = Binder.clearCallingIdentity();
try {
- if (args.length > 0 && "--proto".equals(args[0])) {
- mFingerprint21.dumpProto(fd);
- } else {
- mFingerprint21.dumpInternal(pw);
+ for (ServiceProvider provider : mServiceProviders) {
+ for (FingerprintSensorProperties props : provider.getSensorProperties()) {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ provider.dumpProto(props.sensorId, fd);
+ } else {
+ provider.dumpInternal(props.sensorId, pw);
+ }
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -294,11 +370,12 @@
final long token = Binder.clearCallingIdentity();
try {
- if (mFingerprint21 == null) {
- Slog.e(TAG, "No HAL, caller: " + opPackageName);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for isHardwareDetected, caller: " + opPackageName);
return false;
}
- return mFingerprint21.isHardwareDetected();
+ return provider.second.isHardwareDetected(provider.first);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -311,7 +388,13 @@
return;
}
- mFingerprint21.rename(fingerId, userId, name);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for rename");
+ return;
+ }
+
+ provider.second.rename(provider.first, fingerId, userId, name);
}
@Override // Binder call
@@ -325,7 +408,8 @@
if (userId != UserHandle.getCallingUserId()) {
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- return mFingerprint21.getEnrolledFingerprints(userId);
+
+ return FingerprintService.this.getEnrolledFingerprints(userId, opPackageName);
}
@Override // Binder call
@@ -339,19 +423,32 @@
if (userId != UserHandle.getCallingUserId()) {
Utils.checkPermission(getContext(), INTERACT_ACROSS_USERS);
}
- return mFingerprint21.getEnrolledFingerprints(userId).size() > 0;
+ return !FingerprintService.this.getEnrolledFingerprints(userId, opPackageName)
+ .isEmpty();
}
@Override // Binder call
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- return mFingerprint21.getLockoutModeForUser(userId);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for getLockoutModeForUser");
+ return LockoutTracker.LOCKOUT_NONE;
+ }
+ return provider.second.getLockoutModeForUser(provider.first, userId);
}
@Override // Binder call
public long getAuthenticatorId(int userId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- return mFingerprint21.getAuthenticatorId(userId);
+
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for getAuthenticatorId");
+ return 0;
+ }
+ return provider.second.getAuthenticatorId(provider.first, userId);
}
@Override // Binder call
@@ -359,12 +456,13 @@
@Nullable byte [] hardwareAuthToken, String opPackageName) {
Utils.checkPermission(getContext(), RESET_FINGERPRINT_LOCKOUT);
- if (sensorId == mFingerprint21.getFingerprintSensorProperties().sensorId) {
- mFingerprint21.scheduleResetLockout(userId);
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for resetLockout, caller: " + opPackageName);
return;
}
- Slog.w(TAG, "No matching sensor for resetLockout, sensorId: " + sensorId);
+ provider.second.scheduleResetLockout(sensorId, userId, hardwareAuthToken);
}
@Override
@@ -389,35 +487,52 @@
public void initializeConfiguration(int sensorId, int strength) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ final Fingerprint21 fingerprint21;
if ((Build.IS_USERDEBUG || Build.IS_ENG)
&& getContext().getResources().getBoolean(R.bool.allow_test_udfps)
&& Settings.Secure.getIntForUser(getContext().getContentResolver(),
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
UserHandle.USER_CURRENT) != 0) {
- mFingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId,
+ fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), sensorId,
strength, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
} else {
- mFingerprint21 = Fingerprint21.newInstance(getContext(), sensorId, strength,
+ fingerprint21 = Fingerprint21.newInstance(getContext(), sensorId, strength,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
+ mServiceProviders.add(fingerprint21);
}
@Override
- public void onFingerDown(int x, int y, float minor, float major) {
+ public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFingerprint21.onFingerDown(x, y, minor, major);
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId);
+ return;
+ }
+ provider.onFingerDown(sensorId, x, y, minor, major);
}
@Override
- public void onFingerUp() {
+ public void onFingerUp(int sensorId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFingerprint21.onFingerUp();
+
+ final ServiceProvider provider = getProviderForSensor(sensorId);
+ if (provider == null) {
+ Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId);
+ return;
+ }
+ provider.onFingerUp(sensorId);
}
@Override
- public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFingerprint21.setUdfpsOverlayController(controller);
+
+ for (ServiceProvider provider : mServiceProviders) {
+ provider.setUdfpsOverlayController(controller);
+ }
}
}
@@ -427,6 +542,7 @@
mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher();
mLockoutResetDispatcher = new LockoutResetDispatcher(context);
mLockPatternUtils = new LockPatternUtils(context);
+ mServiceProviders = new ArrayList<>();
}
@Override
@@ -434,6 +550,61 @@
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
}
+ @Nullable
+ private ServiceProvider getProviderForSensor(int sensorId) {
+ for (ServiceProvider provider : mServiceProviders) {
+ if (provider.containsSensor(sensorId)) {
+ return provider;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * For devices with only a single provider, returns that provider. If no providers, or multiple
+ * providers exist, returns null.
+ */
+ @Nullable
+ private Pair<Integer, ServiceProvider> getSingleProvider() {
+ final List<FingerprintSensorProperties> properties = getSensorProperties();
+ if (properties.size() != 1) {
+ return null;
+ }
+
+ // Theoretically we can just return the first provider, but maybe this is easier to
+ // understand.
+ final int sensorId = properties.get(0).sensorId;
+ for (ServiceProvider provider : mServiceProviders) {
+ if (provider.containsSensor(sensorId)) {
+ return new Pair<>(sensorId, provider);
+ }
+ }
+
+ Slog.e(TAG, "Single sensor, but provider not found");
+ return null;
+ }
+
+ @NonNull
+ private List<FingerprintSensorProperties> getSensorProperties() {
+ final List<FingerprintSensorProperties> properties = new ArrayList<>();
+
+ for (ServiceProvider provider : mServiceProviders) {
+ properties.addAll(provider.getSensorProperties());
+ }
+ return properties;
+ }
+
+ @NonNull
+ private List<Fingerprint> getEnrolledFingerprints(int userId, String opPackageName) {
+ final Pair<Integer, ServiceProvider> provider = getSingleProvider();
+ if (provider == null) {
+ Slog.w(TAG, "Null provider for getEnrolledFingerprints, caller: " + opPackageName);
+ return Collections.emptyList();
+ }
+
+ return provider.second.getEnrolledFingerprints(provider.first, userId);
+ }
+
/**
* Checks for public API invocations to ensure that permissions, etc are granted/correct.
*/
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
new file mode 100644
index 0000000..1fcc58c
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.fingerprint;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorProperties;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.IBinder;
+import android.view.Surface;
+
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Superset of features/functionalities that HALs provide to the rest of the framework. This is
+ * more or less mapped to the public and private APIs that {@link FingerprintManager} provide, and
+ * is used at the system server layer to provide easy mapping between request and provider.
+ *
+ * Note that providers support both single-sensor and multi-sensor HALs. In either case,
+ * {@link FingerprintService} must ensure that providers are only requested to perform operations
+ * on sensors that they own.
+ *
+ * For methods other than {@link #containsSensor(int)}, the caller must ensure that the sensorId
+ * passed in is supported by the provider. For example,
+ * if (serviceProvider.containsSensor(sensorId)) {
+ * serviceProvider.operation(sensorId, ...);
+ * }
+ *
+ * For operations that are supported by some providers but not others, clients are required
+ * to check (e.g. via {@link FingerprintManager#getSensorProperties()}) to ensure that the code
+ * path isn't taken. ServiceProviders will provide a no-op for unsupported operations to
+ * fail safely.
+ */
+@SuppressWarnings("deprecation")
+public interface ServiceProvider {
+ /**
+ * Checks if the specified sensor is owned by this provider.
+ */
+ boolean containsSensor(int sensorId);
+
+ @NonNull List<FingerprintSensorProperties> getSensorProperties();
+
+ void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken);
+
+ void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, String opPackageName);
+
+ void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
+ @NonNull String opPackageName);
+
+ void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId,
+ @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
+ @Nullable Surface surface);
+
+ void cancelEnrollment(int sensorId, @NonNull IBinder token);
+
+ void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
+ @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
+ @Nullable Surface surface, int statsClient);
+
+ void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
+ int cookie, @NonNull ClientMonitorCallbackConverter callback,
+ @NonNull String opPackageName, boolean restricted, int statsClient, boolean isKeyguard);
+
+ void startPreparedClient(int sensorId, int cookie);
+
+ void cancelAuthentication(int sensorId, @NonNull IBinder token);
+
+ void scheduleRemove(int sensorId, @NonNull IBinder token,
+ @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
+ @NonNull String opPackageName);
+
+ boolean isHardwareDetected(int sensorId);
+
+ void rename(int sensorId, int fingerId, int userId, @NonNull String name);
+
+ @NonNull List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId);
+
+ @LockoutTracker.LockoutMode int getLockoutModeForUser(int sensorId, int userId);
+
+ long getAuthenticatorId(int sensorId, int userId);
+
+ void onFingerDown(int sensorId, int x, int y, float minor, float major);
+
+ void onFingerUp(int sensorId);
+
+ void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller);
+
+ void dumpProto(int sensorId, @NonNull FileDescriptor fd);
+
+ void dumpInternal(int sensorId, @NonNull PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index c87bfec..30cbf40 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -66,6 +66,7 @@
import com.android.server.biometrics.sensors.RemovalConsumer;
import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.ServiceProvider;
import org.json.JSONArray;
import org.json.JSONException;
@@ -83,14 +84,14 @@
* Supports a single instance of the {@link android.hardware.biometrics.fingerprint.V2_1} or
* its extended minor versions.
*/
-public class Fingerprint21 implements IHwBinder.DeathRecipient {
+public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider {
private static final String TAG = "Fingerprint21";
private static final int ENROLL_TIMEOUT_SEC = 60;
final Context mContext;
private final IActivityTaskManager mActivityTaskManager;
- private final FingerprintSensorProperties mSensorProperties;
+ @NonNull private final FingerprintSensorProperties mSensorProperties;
private final BiometricScheduler mScheduler;
private final Handler mHandler;
private final LockoutResetDispatcher mLockoutResetDispatcher;
@@ -435,9 +436,6 @@
@Nullable IUdfpsOverlayController getUdfpsOverlayController() {
return mUdfpsOverlayController;
}
- @LockoutTracker.LockoutMode public int getLockoutModeForUser(int userId) {
- return mLockoutTracker.getLockoutModeForUser(userId);
- }
private void scheduleLoadAuthenticatorIds() {
// Note that this can be performed on the scheduler (as opposed to being done immediately
@@ -466,7 +464,8 @@
* correct.
*/
private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
- final boolean hasEnrolled = !getEnrolledFingerprints(targetUserId).isEmpty();
+ final boolean hasEnrolled =
+ !getEnrolledFingerprints(mSensorProperties.sensorId, targetUserId).isEmpty();
final FingerprintUpdateActiveUserClient client =
new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
mContext.getOpPackageName(), mSensorProperties.sensorId, mCurrentUserId,
@@ -481,7 +480,21 @@
});
}
- public void scheduleResetLockout(int userId) {
+ @Override
+ public boolean containsSensor(int sensorId) {
+ return mSensorProperties.sensorId == sensorId;
+ }
+
+ @Override
+ @NonNull
+ public List<FingerprintSensorProperties> getSensorProperties() {
+ final List<FingerprintSensorProperties> properties = new ArrayList<>();
+ properties.add(mSensorProperties);
+ return properties;
+ }
+
+ @Override
+ public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
// Fingerprint2.1 keeps track of lockout in the framework. Let's just do it on the handler
// thread.
mHandler.post(() -> {
@@ -489,7 +502,8 @@
});
}
- public void scheduleGenerateChallenge(@NonNull IBinder token,
+ @Override
+ public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) {
mHandler.post(() -> {
final FingerprintGenerateChallengeClient client =
@@ -500,7 +514,9 @@
});
}
- public void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String opPackageName) {
+ @Override
+ public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token,
+ @NonNull String opPackageName) {
mHandler.post(() -> {
final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(
mContext, mLazyDaemon, token, opPackageName, mSensorProperties.sensorId);
@@ -508,7 +524,9 @@
});
}
- public void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId,
+ @Override
+ public void scheduleEnroll(int sensorId, @NonNull IBinder token,
+ @NonNull byte[] hardwareAuthToken, int userId,
@NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
@Nullable Surface surface) {
mHandler.post(() -> {
@@ -531,13 +549,15 @@
});
}
- public void cancelEnrollment(@NonNull IBinder token) {
+ @Override
+ public void cancelEnrollment(int sensorId, @NonNull IBinder token) {
mHandler.post(() -> {
mScheduler.cancelEnrollment(token);
});
}
- public void scheduleFingerDetect(@NonNull IBinder token, int userId,
+ @Override
+ public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
@NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
@Nullable Surface surface, int statsClient) {
mHandler.post(() -> {
@@ -552,8 +572,9 @@
});
}
- public void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId,
- int cookie, @NonNull ClientMonitorCallbackConverter listener,
+ @Override
+ public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
+ int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean isKeyguard) {
mHandler.post(() -> {
@@ -569,19 +590,22 @@
});
}
- public void startPreparedClient(int cookie) {
+ @Override
+ public void startPreparedClient(int sensorId, int cookie) {
mHandler.post(() -> {
mScheduler.startPreparedClient(cookie);
});
}
- public void cancelAuthentication(@NonNull IBinder token) {
+ @Override
+ public void cancelAuthentication(int sensorId, @NonNull IBinder token) {
mHandler.post(() -> {
mScheduler.cancelAuthentication(token);
});
}
- public void scheduleRemove(@NonNull IBinder token,
+ @Override
+ public void scheduleRemove(int sensorId, @NonNull IBinder token,
@NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId,
@NonNull String opPackageName) {
mHandler.post(() -> {
@@ -599,7 +623,8 @@
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
- final List<Fingerprint> enrolledList = getEnrolledFingerprints(userId);
+ final List<Fingerprint> enrolledList = getEnrolledFingerprints(
+ mSensorProperties.sensorId, userId);
final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
mContext, mLazyDaemon, userId, mContext.getOpPackageName(),
mSensorProperties.sensorId, enrolledList, FingerprintUtils.getInstance(),
@@ -608,30 +633,37 @@
});
}
- public boolean isHardwareDetected() {
+ @Override
+ public boolean isHardwareDetected(int sensorId) {
final IBiometricsFingerprint daemon = getDaemon();
return daemon != null;
}
- @NonNull public FingerprintSensorProperties getFingerprintSensorProperties() {
- return mSensorProperties;
- }
-
- public void rename(int fingerId, int userId, String name) {
+ @Override
+ public void rename(int sensorId, int fingerId, int userId, @NonNull String name) {
mHandler.post(() -> {
FingerprintUtils.getInstance().renameBiometricForUser(mContext, userId, fingerId, name);
});
}
- public List<Fingerprint> getEnrolledFingerprints(int userId) {
+ @Override
+ @NonNull
+ public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) {
return FingerprintUtils.getInstance().getBiometricsForUser(mContext, userId);
}
- public long getAuthenticatorId(int userId) {
+ @Override
+ @LockoutTracker.LockoutMode public int getLockoutModeForUser(int sensorId, int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ @Override
+ public long getAuthenticatorId(int sensorId, int userId) {
return mAuthenticatorIds.get(userId);
}
- public void onFingerDown(int x, int y, float minor, float major) {
+ @Override
+ public void onFingerDown(int sensorId, 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);
@@ -641,7 +673,8 @@
udfps.onFingerDown(x, y, minor, major);
}
- public void onFingerUp() {
+ @Override
+ public void onFingerUp(int sensorId) {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof Udfps)) {
Slog.w(TAG, "onFingerDown received during client: " + client);
@@ -651,11 +684,13 @@
udfps.onFingerUp();
}
- public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ @Override
+ public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) {
mUdfpsOverlayController = controller;
}
- public void dumpProto(FileDescriptor fd) {
+ @Override
+ public void dumpProto(int sensorId, FileDescriptor fd) {
PerformanceTracker tracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
@@ -695,7 +730,8 @@
tracker.clear();
}
- public void dumpInternal(@NonNull PrintWriter pw) {
+ @Override
+ public void dumpInternal(int sensorId, @NonNull PrintWriter pw) {
PerformanceTracker performanceTracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 6d8f241..d68671b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -46,6 +46,7 @@
import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import java.util.ArrayList;
+import java.util.List;
import java.util.Random;
/**
@@ -397,8 +398,9 @@
// Schedule this only after we invoke onClientFinished for the previous client, so that
// internal preemption logic is not run.
- mFingerprint21.scheduleAuthenticate(token, operationId, user, cookie,
- listener, opPackageName, restricted, statsClient, isKeyguard);
+ mFingerprint21.scheduleAuthenticate(mFingerprint21.mSensorProperties.sensorId, token,
+ operationId, user, cookie, listener, opPackageName, restricted, statsClient,
+ isKeyguard);
}
}
@@ -451,12 +453,14 @@
@Override
@NonNull
- public FingerprintSensorProperties getFingerprintSensorProperties() {
- return mSensorProperties;
+ public List<FingerprintSensorProperties> getSensorProperties() {
+ final List<FingerprintSensorProperties> properties = new ArrayList<>();
+ properties.add(mSensorProperties);
+ return properties;
}
@Override
- public void onFingerDown(int x, int y, float minor, float major) {
+ public void onFingerDown(int sensorId, int x, int y, float minor, float major) {
mHandler.post(() -> {
Slog.d(TAG, "onFingerDown");
final AuthenticationConsumer lastAuthenticatedConsumer =
@@ -503,7 +507,7 @@
}
@Override
- public void onFingerUp() {
+ public void onFingerUp(int sensorId) {
mHandler.post(() -> {
Slog.d(TAG, "onFingerUp");
@@ -558,7 +562,7 @@
// Things can happen before SysUI loads and sets the controller.
if (controller != null) {
Slog.d(TAG, "setDebugMessage: " + message);
- controller.setDebugMessage(message);
+ controller.setDebugMessage(mSensorProperties.sensorId, message);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when sending message: " + message, e);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 8087e15..0658f95 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -79,7 +79,7 @@
if (authenticated) {
resetFailedAttempts(getTargetUserId());
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, true /* success */);
} else {
final @LockoutTracker.LockoutMode int lockoutMode =
@@ -92,7 +92,7 @@
// Send the error, but do not invoke the FinishCallback yet. Since lockout is not
// controlled by the HAL, the framework must stop the sensor before finishing the
// client.
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
onErrorInternal(errorCode, 0 /* vendorCode */, false /* finish */);
cancel();
}
@@ -111,7 +111,7 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
// GroupId was never used. In fact, groupId is always the same as userId.
getFreshDaemon().authenticate(mOperationId, getTargetUserId());
@@ -119,14 +119,14 @@
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index 5865617..cad2214 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -61,7 +61,7 @@
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
@@ -80,14 +80,14 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
getFreshDaemon().authenticate(0 /* operationId */, getTargetUserId());
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting auth", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, false /* success */);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index 1b9fae9..b1030bf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -70,7 +70,7 @@
@Override
protected void startHalOperation() {
- UdfpsHelper.showUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
// GroupId was never used. In fact, groupId is always the same as userId.
getFreshDaemon().enroll(mHardwareAuthToken, getTargetUserId(), mTimeoutSec);
@@ -78,14 +78,14 @@
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
mCallback.onClientFinished(this, false /* success */);
}
}
@Override
protected void stopHalOperation() {
- UdfpsHelper.hideUdfpsOverlay(mUdfpsOverlayController);
+ UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController);
try {
getFreshDaemon().cancel();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
index c71ecbf..0f1d6b4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/UdfpsHelper.java
@@ -62,23 +62,25 @@
}
}
- static void showUdfpsOverlay(@Nullable IUdfpsOverlayController udfpsOverlayController) {
+ static void showUdfpsOverlay(int sensorId,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
if (udfpsOverlayController == null) {
return;
}
try {
- udfpsOverlayController.showUdfpsOverlay();
+ udfpsOverlayController.showUdfpsOverlay(sensorId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e);
}
}
- static void hideUdfpsOverlay(@Nullable IUdfpsOverlayController udfpsOverlayController) {
+ static void hideUdfpsOverlay(int sensorId,
+ @Nullable IUdfpsOverlayController udfpsOverlayController) {
if (udfpsOverlayController == null) {
return;
}
try {
- udfpsOverlayController.hideUdfpsOverlay();
+ udfpsOverlayController.hideUdfpsOverlay(sensorId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when hiding the UDFPS overlay", e);
}
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index 62630300..fa03e59 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -60,12 +60,12 @@
import android.net.ipsec.ike.TunnelModeChildSessionParams;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
-import android.net.util.IpRange;
import android.system.OsConstants;
import android.util.Log;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.HexDump;
+import com.android.net.module.util.IpRange;
import java.net.Inet4Address;
import java.net.Inet6Address;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index f2f6dbe..6e6d848 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -58,11 +58,6 @@
// If true, turn off TV upon standby. False by default.
private boolean mAutoTvOff;
- // Local active port number used for Routing Control.
- // Default 0 means HOME is the current active path. Temp solution only.
- // TODO(amyjojo): adding system constants for input ports to TIF mapping.
- private int mLocalActivePath = 0;
-
// Determines what action should be taken upon receiving Routing Control messages.
@VisibleForTesting
protected HdmiProperties.playback_device_action_on_routing_control_values
@@ -438,16 +433,6 @@
checkIfPendingActionsCleared();
}
- private void routeToPort(int portId) {
- // TODO(AMYJOJO): route to specific input of the port
- mLocalActivePath = portId;
- }
-
- @VisibleForTesting
- protected int getLocalActivePath() {
- return mLocalActivePath;
- }
-
@Override
protected void dump(final IndentingPrintWriter pw) {
super.dump(pw);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 7c98c6c..cc8a330 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -46,6 +46,7 @@
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
+import android.hardware.input.InputManagerInternal.LidSwitchCallback;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
import android.media.AudioManager;
@@ -189,6 +190,10 @@
private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
private int mNextVibratorTokenValue;
+ // State for lid switch
+ private final Object mLidSwitchLock = new Object();
+ private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
+
// State for the currently installed input filter.
final Object mInputFilterLock = new Object();
IInputFilter mInputFilter; // guarded by mInputFilterLock
@@ -233,7 +238,7 @@
private static native void nativeToggleCapsLock(long ptr, int deviceId);
private static native void nativeDisplayRemoved(long ptr, int displayId);
private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
- private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
+ private static native void nativeSetSystemUiLightsOut(long ptr, boolean lightsOut);
private static native void nativeSetFocusedApplication(long ptr,
int displayId, InputApplicationHandle application);
private static native void nativeSetFocusedDisplay(long ptr, int displayId);
@@ -330,6 +335,9 @@
public static final int SW_CAMERA_LENS_COVER_BIT = 1 << SW_CAMERA_LENS_COVER;
public static final int SW_MUTE_DEVICE_BIT = 1 << SW_MUTE_DEVICE;
+ /** Indicates an open state for the lid switch. */
+ public static final int SW_STATE_LID_OPEN = 0;
+
/** Whether to use the dev/input/event or uevent subsystem for the audio jack. */
final boolean mUseDevInputEventForAudioJack;
@@ -353,13 +361,33 @@
}
public void setWindowManagerCallbacks(WindowManagerCallbacks callbacks) {
+ if (mWindowManagerCallbacks != null) {
+ unregisterLidSwitchCallbackInternal(mWindowManagerCallbacks);
+ }
mWindowManagerCallbacks = callbacks;
+ registerLidSwitchCallbackInternal(mWindowManagerCallbacks);
}
public void setWiredAccessoryCallbacks(WiredAccessoryCallbacks callbacks) {
mWiredAccessoryCallbacks = callbacks;
}
+ void registerLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+ boolean lidOpen;
+ synchronized (mLidSwitchLock) {
+ mLidSwitchCallbacks.add(callback);
+ lidOpen = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY, SW_LID)
+ == SW_STATE_LID_OPEN;
+ }
+ callback.notifyLidSwitchChanged(0 /* whenNanos */, lidOpen);
+ }
+
+ void unregisterLidSwitchCallbackInternal(@NonNull LidSwitchCallback callback) {
+ synchronized (mLidSwitchLock) {
+ mLidSwitchCallbacks.remove(callback);
+ }
+ }
+
public void start() {
Slog.i(TAG, "Starting input manager");
nativeStart(mPtr);
@@ -1560,8 +1588,8 @@
nativeSetInputDispatchMode(mPtr, enabled, frozen);
}
- public void setSystemUiVisibility(int visibility) {
- nativeSetSystemUiVisibility(mPtr, visibility);
+ public void setSystemUiLightsOut(boolean lightsOut) {
+ nativeSetSystemUiLightsOut(mPtr, lightsOut);
}
/**
@@ -1934,6 +1962,7 @@
synchronized (mInputFilterLock) { }
synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
synchronized (mGestureMonitorPidsLock) { /* Test if blocked by gesture monitor pids lock */}
+ synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
nativeMonitor(mPtr);
}
@@ -1964,7 +1993,15 @@
if ((switchMask & SW_LID_BIT) != 0) {
final boolean lidOpen = ((switchValues & SW_LID_BIT) == 0);
- mWindowManagerCallbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+
+ ArrayList<LidSwitchCallback> callbacksCopy;
+ synchronized (mLidSwitchLock) {
+ callbacksCopy = new ArrayList<>(mLidSwitchCallbacks);
+ }
+ for (int i = 0; i < callbacksCopy.size(); i++) {
+ LidSwitchCallback callbacks = callbacksCopy.get(i);
+ callbacks.notifyLidSwitchChanged(whenNanos, lidOpen);
+ }
}
if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
@@ -2263,20 +2300,13 @@
/**
* Callback interface implemented by the Window Manager.
*/
- public interface WindowManagerCallbacks {
+ public interface WindowManagerCallbacks extends LidSwitchCallback {
/**
* This callback is invoked when the confuguration changes.
*/
public void notifyConfigurationChanged();
/**
- * This callback is invoked when the lid switch changes state.
- * @param whenNanos the time when the change occurred
- * @param lidOpen true if the lid is open
- */
- public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
-
- /**
* This callback is invoked when the camera lens cover switch changes state.
* @param whenNanos the time when the change occurred
* @param lensCovered true is the lens is covered
@@ -2603,6 +2633,16 @@
@NonNull IBinder toChannelToken) {
return InputManagerService.this.transferTouchFocus(fromChannelToken, toChannelToken);
}
+
+ @Override
+ public void registerLidSwitchCallback(LidSwitchCallback callbacks) {
+ registerLidSwitchCallbackInternal(callbacks);
+ }
+
+ @Override
+ public void unregisterLidSwitchCallback(LidSwitchCallback callbacks) {
+ unregisterLidSwitchCallbackInternal(callbacks);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index a44fabbe..2eccaf1 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -17,6 +17,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.CLIENTS;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorProto.ELAPSED_REALTIME_NANOS;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodEditorTraceFileProto.ENTRY;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -96,12 +99,15 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.imetracing.ImeTracing;
+import android.util.proto.ProtoOutputStream;
import android.view.ContextThemeWrapper;
import android.view.DisplayInfo;
import android.view.IWindowManager;
@@ -4032,6 +4038,55 @@
mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
}
+ /**
+ * Starting point for dumping the IME tracing information in proto format.
+ *
+ * @param clientProtoDump dump information from the IME client side
+ */
+ @BinderThread
+ @Override
+ public void startProtoDump(byte[] clientProtoDump) {
+ if (!ImeTracing.getInstance().isAvailable() || !ImeTracing.getInstance().isEnabled()) {
+ return;
+ }
+ if (clientProtoDump == null && mCurClient == null) {
+ return;
+ }
+
+ ProtoOutputStream proto = new ProtoOutputStream();
+ final long token = proto.start(ENTRY);
+ proto.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+ // TODO: get server side dump
+ if (clientProtoDump != null) {
+ proto.write(CLIENTS, clientProtoDump);
+ } else {
+ IBinder client = null;
+
+ synchronized (mMethodMap) {
+ if (mCurClient != null && mCurClient.client != null) {
+ client = mCurClient.client.asBinder();
+ }
+ }
+
+ if (client != null) {
+ try {
+ proto.write(CLIENTS,
+ TransferPipe.dumpAsync(client, ImeTracing.PROTO_ARG));
+ } catch (IOException | RemoteException e) {
+ Log.e(TAG, "Exception while collecting client side ime dump", e);
+ }
+ }
+ }
+ proto.end(token);
+ ImeTracing.getInstance().addToBuffer(proto);
+ }
+
+ @BinderThread
+ @Override
+ public boolean isImeTraceEnabled() {
+ return ImeTracing.getInstance().isEnabled();
+ }
+
@BinderThread
private void notifyUserAction(@NonNull IBinder token) {
if (DEBUG) {
@@ -5426,6 +5481,21 @@
return mService.handleShellCommandSetInputMethod(this);
case "reset":
return mService.handleShellCommandResetInputMethod(this);
+ case "tracing":
+ int result = ImeTracing.getInstance().onShellCommand(this);
+ boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
+ for (ClientState state : mService.mClients.values()) {
+ if (state != null) {
+ try {
+ state.client.setImeTraceEnabled(isImeTraceEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG,
+ "Error while trying to enable/disable ime "
+ + "trace on client window", e);
+ }
+ }
+ }
+ return result;
default:
getOutPrintWriter().println("Unknown command: " + imeCommand);
return ShellCommandResult.FAILURE;
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index b518eb1..a6ca25b 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1805,5 +1805,16 @@
mUserDataMap.dump(fd, ipw, args);
}
}
+
+ @BinderThread
+ @Override
+ public void startProtoDump(byte[] clientProtoDump) throws RemoteException {
+ }
+
+ @BinderThread
+ @Override
+ public boolean isImeTraceEnabled() throws RemoteException {
+ return false;
+ }
}
}
diff --git a/services/core/java/com/android/server/media/MediaResourceMonitorService.java b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
index e5da9b1..fc7dd30 100644
--- a/services/core/java/com/android/server/media/MediaResourceMonitorService.java
+++ b/services/core/java/com/android/server/media/MediaResourceMonitorService.java
@@ -28,6 +28,8 @@
import com.android.server.SystemService;
+import java.util.List;
+
/** This class provides a system service that monitors media resource usage. */
public class MediaResourceMonitorService extends SystemService {
private static final String TAG = "MediaResourceMonitor";
@@ -60,16 +62,18 @@
if (pkgNames == null) {
return;
}
- UserManager manager = getContext().getSystemService(UserManager.class);
- int[] userIds = manager.getEnabledProfileIds(ActivityManager.getCurrentUser());
- if (userIds == null || userIds.length == 0) {
+ UserManager manager = getContext().createContextAsUser(
+ UserHandle.of(ActivityManager.getCurrentUser()), /*flags=*/0)
+ .getSystemService(UserManager.class);
+ List<UserHandle> enabledProfiles = manager.getEnabledProfiles();
+ if (enabledProfiles.isEmpty()) {
return;
}
Intent intent = new Intent(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
intent.putExtra(Intent.EXTRA_PACKAGES, pkgNames);
intent.putExtra(Intent.EXTRA_MEDIA_RESOURCE_TYPE, type);
- for (int userId : userIds) {
- getContext().sendBroadcastAsUser(intent, UserHandle.of(userId),
+ for (UserHandle userHandle : enabledProfiles) {
+ getContext().sendBroadcastAsUser(intent, userHandle,
android.Manifest.permission.RECEIVE_MEDIA_RESOURCE_USAGE);
}
} finally {
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index 20df271..69c57a9 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -38,6 +38,7 @@
import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.List;
@@ -53,11 +54,13 @@
private ISessionManager mSessionService;
private PrintWriter mWriter;
private PrintWriter mErrorWriter;
+ private InputStream mInput;
@Override
public int onCommand(String cmd) {
mWriter = getOutPrintWriter();
mErrorWriter = getErrPrintWriter();
+ mInput = getRawInputStream();
if (TextUtils.isEmpty(cmd)) {
return handleDefaultCommands(cmd);
@@ -189,6 +192,10 @@
KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
}
+ void log(String code, String msg) {
+ mWriter.println(code + " " + msg);
+ }
+
void showError(String errMsg) {
onHelp();
mErrorWriter.println(errMsg);
@@ -273,11 +280,14 @@
cbThread.start();
try {
- InputStreamReader converter = new InputStreamReader(System.in);
+ InputStreamReader converter = new InputStreamReader(mInput);
BufferedReader in = new BufferedReader(converter);
String line;
- while ((line = in.readLine()) != null) {
+ while (true) {
+ mWriter.flush();
+ mErrorWriter.flush();
+ if ((line = in.readLine()) == null) break;
boolean addNewline = true;
if (line.length() <= 0) {
addNewline = false;
@@ -297,7 +307,7 @@
synchronized (this) {
if (addNewline) {
- System.out.println("");
+ mWriter.println("");
}
printUsageMessage();
}
diff --git a/services/core/java/com/android/server/media/VolumeCtrl.java b/services/core/java/com/android/server/media/VolumeCtrl.java
index 7a26665..d516d96 100644
--- a/services/core/java/com/android/server/media/VolumeCtrl.java
+++ b/services/core/java/com/android/server/media/VolumeCtrl.java
@@ -32,6 +32,8 @@
public class VolumeCtrl {
private static final String TAG = "VolumeCtrl";
+ private static final String LOG_V = "[V]";
+ private static final String LOG_E = "[E]";
// --stream affects --set, --adj or --get options.
// --show affects --set and --adj options.
@@ -80,21 +82,22 @@
break;
case "--get":
doGet = true;
- log(LOG_V, "will get volume");
+ cmd.log(LOG_V, "will get volume");
break;
case "--stream":
stream = Integer.decode(cmd.getNextArgRequired()).intValue();
- log(LOG_V, "will control stream=" + stream + " (" + streamName(stream) + ")");
+ cmd.log(LOG_V,
+ "will control stream=" + stream + " (" + streamName(stream) + ")");
break;
case "--set":
volIndex = Integer.decode(cmd.getNextArgRequired()).intValue();
mode = VOLUME_CONTROL_MODE_SET;
- log(LOG_V, "will set volume to index=" + volIndex);
+ cmd.log(LOG_V, "will set volume to index=" + volIndex);
break;
case "--adj":
mode = VOLUME_CONTROL_MODE_ADJUST;
adjustment = cmd.getNextArgRequired();
- log(LOG_V, "will adjust volume");
+ cmd.log(LOG_V, "will adjust volume");
break;
default:
throw new IllegalArgumentException("Unknown argument " + option);
@@ -122,11 +125,11 @@
//----------------------------------------
// Test initialization
- log(LOG_V, "Connecting to AudioService");
+ cmd.log(LOG_V, "Connecting to AudioService");
IAudioService audioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
Context.AUDIO_SERVICE));
if (audioService == null) {
- System.err.println(BaseCommand.NO_SYSTEM_ERROR_CODE);
+ cmd.log(LOG_E, BaseCommand.NO_SYSTEM_ERROR_CODE);
throw new AndroidException(
"Can't connect to audio service; is the system running?");
}
@@ -152,23 +155,12 @@
audioService.adjustStreamVolume(stream, adjDir, flag, pack);
}
if (doGet) {
- log(LOG_V, "volume is " + audioService.getStreamVolume(stream)
+ cmd.log(LOG_V, "volume is " + audioService.getStreamVolume(stream)
+ " in range [" + audioService.getStreamMinVolume(stream)
+ ".." + audioService.getStreamMaxVolume(stream) + "]");
}
}
- //--------------------------------------------
- // Utilities
-
- static final String LOG_V = "[v]";
- static final String LOG_W = "[w]";
- static final String LOG_OK = "[ok]";
-
- static void log(String code, String msg) {
- System.out.println(code + " " + msg);
- }
-
static String streamName(int stream) {
try {
return AudioSystem.STREAM_NAMES[stream];
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 60737bf..949dcb25 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2081,7 +2081,14 @@
mRelinquished = true;
- return mPm.new VerificationParams(user, stageDir, localObserver, params,
+ // TODO(b/169375643): Remove this workaround once b/161121612 is fixed.
+ PackageInstaller.SessionParams copiedParams = params.copy();
+ if (params.isStaged) {
+ // This is called by the pre-reboot verification. Don't enable rollback here since
+ // it has been enabled when pre-reboot verification starts.
+ copiedParams.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+ }
+ return mPm.new VerificationParams(user, stageDir, localObserver, copiedParams,
mInstallSource, mInstallerUid, mSigningDetails, sessionId);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b451eaf1..1ae1681 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6214,8 +6214,11 @@
synchronized (mLock) {
final AndroidPackage p = mPackages.get(packageName);
+ if (p == null) {
+ return false;
+ }
final PackageSetting ps = getPackageSetting(p.getPackageName());
- if (p == null || ps == null) {
+ if (ps == null) {
return false;
}
final int callingUid = Binder.getCallingUid();
@@ -11291,6 +11294,8 @@
mSettings.addRenamedPackageLPw(parsedPackage.getRealPackage(),
originalPkgSetting.name);
mTransferredPackages.add(originalPkgSetting.name);
+ } else {
+ mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
}
}
if (pkgSetting.sharedUser != null) {
@@ -25947,6 +25952,15 @@
mPermissionManager.writeStateToPackageSettingsTEMP();
mSettings.writeLPr();
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.INJECT_EVENTS, "holdLock requires shell identity");
+ synchronized (mLock) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index bae36b2a..a922d76 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -485,6 +485,10 @@
return mRenamedPackages.put(pkgName, origPkgName);
}
+ void removeRenamedPackageLPw(String pkgName) {
+ mRenamedPackages.remove(pkgName);
+ }
+
public boolean canPropagatePermissionToInstantApp(String permName) {
return mPermissions.canPropagatePermissionToInstantApp(permName);
}
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 865b8a1..524f9ac 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -425,8 +425,7 @@
public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg,
UidPermissionState uidState) {
- int index = pkg.getRequestedPermissions().indexOf(name);
- if (!uidState.hasRequestedPermission(name) && index == -1) {
+ if (!uidState.hasPermissionState(name) && !pkg.getRequestedPermissions().contains(name)) {
throw new SecurityException("Package " + pkg.getPackageName()
+ " has not requested permission " + name);
}
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 840b233..474ce7c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -943,7 +943,7 @@
private boolean checkSinglePermissionInternal(int uid,
@NonNull UidPermissionState uidState, @NonNull String permissionName) {
- if (!uidState.hasPermission(permissionName)) {
+ if (!uidState.isPermissionGranted(permissionName)) {
return false;
}
@@ -1659,7 +1659,7 @@
}
// Permission is already revoked, no need to do anything.
- if (!uidState.hasPermission(permName)) {
+ if (!uidState.isPermissionGranted(permName)) {
return;
}
@@ -2482,12 +2482,12 @@
return Collections.emptySet();
}
if (!ps.getInstantApp(userId)) {
- return uidState.getPermissions();
+ return uidState.getGrantedPermissions();
} else {
// Install permission state is shared among all users, but instant app state is
// per-user, so we can only filter it here unless we make install permission state
// per-user as well.
- final Set<String> instantPermissions = new ArraySet<>(uidState.getPermissions());
+ final Set<String> instantPermissions = new ArraySet<>(uidState.getGrantedPermissions());
instantPermissions.removeIf(permissionName -> {
BasePermission permission = mSettings.getPermission(permissionName);
if (permission == null) {
@@ -2660,7 +2660,7 @@
// Cache newImplicitPermissions before modifing permissionsState as for the shared
// uids the original and new state are the same object
- if (!origState.hasRequestedPermission(permName)
+ if (!origState.hasPermissionState(permName)
&& (pkg.getImplicitPermissions().contains(permName)
|| (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
if (pkg.getImplicitPermissions().contains(permName)) {
@@ -2685,7 +2685,7 @@
SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
String splitPermName = sp.getSplitPermission();
if (sp.getNewPermissions().contains(permName)
- && origState.hasPermission(splitPermName)) {
+ && origState.isPermissionGranted(splitPermName)) {
upgradedActivityRecognitionPermission = splitPermName;
newImplicitPermissions.add(permName);
@@ -2741,7 +2741,7 @@
}
}
- if (grant == GRANT_INSTALL && !allowedSig && !origState.hasPermission(perm)) {
+ if (grant == GRANT_INSTALL && !allowedSig && !origState.isPermissionGranted(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.
@@ -2849,7 +2849,7 @@
}
}
- if (!uidState.hasPermission(bp.name)
+ if (!uidState.isPermissionGranted(bp.name)
&& uidState.grantPermission(bp)
!= PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
@@ -2993,7 +2993,7 @@
boolean supportsRuntimePermissions = pkg.getTargetSdkVersion()
>= Build.VERSION_CODES.M;
- for (String permission : ps.getPermissions()) {
+ for (String permission : ps.getGrantedPermissions()) {
if (!pkg.getImplicitPermissions().contains(permission)) {
BasePermission bp = mSettings.getPermissionLocked(permission);
if (bp.isRuntime()) {
@@ -3050,7 +3050,7 @@
int numSourcePerm = sourcePerms.size();
for (int i = 0; i < numSourcePerm; i++) {
String sourcePerm = sourcePerms.valueAt(i);
- if (ps.hasPermission(sourcePerm)) {
+ if (ps.isPermissionGranted(sourcePerm)) {
if (!isGranted) {
flags = 0;
}
@@ -3155,7 +3155,7 @@
}
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- if (!origPs.hasRequestedPermission(sourcePerms)) {
+ if (!origPs.hasPermissionState(sourcePerms)) {
boolean inheritsFromInstallPerm = false;
for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size();
sourcePermNum++) {
@@ -3465,7 +3465,7 @@
if (!allowed && bp.isDevelopment()) {
// For development permissions, a development permission
// is granted only if it was already granted.
- allowed = origPermissions.hasPermission(perm);
+ allowed = origPermissions.isPermissionGranted(perm);
}
if (!allowed && bp.isSetup()
&& ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
@@ -3687,7 +3687,7 @@
continue;
}
- if (uidState.hasPermission(permissionName)) {
+ if (uidState.isPermissionGranted(permissionName)) {
if (oldGrantedRestrictedPermissions.get(userId) == null) {
oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
}
@@ -3749,7 +3749,7 @@
// as whitelisting trumps policy i.e. policy cannot grant a non
// grantable permission.
if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isGranted = uidState.hasPermission(permissionName);
+ final boolean isGranted = uidState.isPermissionGranted(permissionName);
if (!isWhitelisted && isGranted) {
mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3791,7 +3791,7 @@
+ " and user " + userId);
continue;
}
- if (!newUidState.hasPermission(permission)) {
+ if (!newUidState.isPermissionGranted(permission)) {
callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
}
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 b45176b..06a7f8d 100644
--- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java
+++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java
@@ -54,119 +54,52 @@
@NonNull
private final Object mLock = new Object();
+ private boolean mMissing;
+
@GuardedBy("mLock")
+ @Nullable
private ArrayMap<String, PermissionState> mPermissions;
+ private boolean mPermissionReviewRequired;
+
@NonNull
private int[] mGlobalGids = NO_GIDS;
- private boolean mMissing;
+ public UidPermissionState() {}
- private boolean mPermissionReviewRequired;
-
- public UidPermissionState() {
- /* do nothing */
- }
-
- public UidPermissionState(@NonNull UidPermissionState prototype) {
- copyFrom(prototype);
- }
-
- /**
- * Gets the global gids, applicable to all users.
- */
- @NonNull
- public int[] getGlobalGids() {
- return mGlobalGids;
- }
-
- /**
- * Sets the global gids, applicable to all users.
- *
- * @param globalGids The global gids.
- */
- public void setGlobalGids(@NonNull int[] globalGids) {
- if (!ArrayUtils.isEmpty(globalGids)) {
- mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
- }
- }
-
- static void invalidateCache() {
- PackageManager.invalidatePackageInfoCache();
- }
-
- /**
- * Initialized this instance from another one.
- *
- * @param other The other instance.
- */
- public void copyFrom(@NonNull UidPermissionState other) {
- if (other == this) {
- return;
- }
-
+ public UidPermissionState(@NonNull UidPermissionState other) {
synchronized (mLock) {
- if (mPermissions != null) {
- if (other.mPermissions == null) {
- mPermissions = null;
- } else {
- mPermissions.clear();
- }
- }
+ mMissing = other.mMissing;
+
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);
- PermissionState permissionState = other.mPermissions.valueAt(i);
+ mPermissions = new ArrayMap<>();
+ final int permissionsSize = other.mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final String name = other.mPermissions.keyAt(i);
+ final PermissionState permissionState = other.mPermissions.valueAt(i);
mPermissions.put(name, new PermissionState(permissionState));
}
}
- }
- mGlobalGids = NO_GIDS;
- if (other.mGlobalGids != NO_GIDS) {
- mGlobalGids = other.mGlobalGids.clone();
- }
+ mPermissionReviewRequired = other.mPermissionReviewRequired;
- mMissing = other.mMissing;
-
- mPermissionReviewRequired = other.mPermissionReviewRequired;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final UidPermissionState other = (UidPermissionState) obj;
-
- synchronized (mLock) {
- if (mPermissions == null) {
- if (other.mPermissions != null) {
- return false;
- }
- } else if (!mPermissions.equals(other.mPermissions)) {
- return false;
+ if (other.mGlobalGids != NO_GIDS) {
+ mGlobalGids = other.mGlobalGids.clone();
}
}
+ }
- if (mMissing != other.mMissing) {
- return false;
+ /**
+ * Reset the internal state of this object.
+ */
+ public void reset() {
+ synchronized (mLock) {
+ mMissing = false;
+ mPermissions = null;
+ mPermissionReviewRequired = false;
+ mGlobalGids = NO_GIDS;
+ invalidateCache();
}
-
- if (mPermissionReviewRequired != other.mPermissionReviewRequired) {
- return false;
- }
- return Arrays.equals(mGlobalGids, other.mGlobalGids);
}
/**
@@ -186,233 +119,45 @@
mMissing = missing;
}
- public boolean isPermissionReviewRequired() {
- return mPermissionReviewRequired;
- }
-
/**
- * Gets whether the state has a given permission.
+ * Get whether there is a permission state for a permission.
*
- * @param name The permission name.
- * @return Whether the state has the permission.
+ * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
*/
- public boolean hasPermission(@NonNull String name) {
+ @Deprecated
+ public boolean hasPermissionState(@NonNull String name) {
synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- PermissionState permissionState = mPermissions.get(name);
- return permissionState != null && permissionState.isGranted();
+ return mPermissions != null && mPermissions.containsKey(name);
}
}
/**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
+ * Get whether there is a permission state for any of the permissions.
*
- * @deprecated Not all requested permissions may be here.
+ * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
*/
@Deprecated
- public boolean hasRequestedPermission(@NonNull ArraySet<String> names) {
+ public boolean hasPermissionState(@NonNull 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) {
+ final int namesSize = names.size();
+ for (int i = 0; i < namesSize; i++) {
+ final String name = names.valueAt(i);
+ if (mPermissions.containsKey(name)) {
return true;
}
}
- }
-
- return false;
- }
-
- /**
- * Returns whether the state has any known request for the given permission name,
- * whether or not it has been granted.
- *
- * @deprecated Not all requested permissions may be here.
- */
- @Deprecated
- public boolean hasRequestedPermission(@NonNull 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.
- *
- * @return The permissions or an empty set.
- */
- @NonNull
- public Set<String> getPermissions() {
- 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 (hasPermission(permission)) {
- permissions.add(permission);
- }
- }
-
- return permissions;
- }
- }
-
- /**
- * Gets the flags for a permission.
- *
- * @param name The permission name.
- * @return The permission state or null if no such.
- */
- public int getPermissionFlags(@NonNull String name) {
- PermissionState permState = getPermissionState(name);
- if (permState != null) {
- return permState.getFlags();
- }
- return 0;
- }
-
- /**
- * Update the flags associated with a given permission.
- * @param permission The permission whose flags 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(@NonNull BasePermission permission, int flagMask,
- int flagValues) {
- if (flagMask == 0) {
return false;
}
-
- PermissionState permissionState = ensurePermissionState(permission);
-
- final int oldFlags = permissionState.getFlags();
-
- synchronized (mLock) {
- final boolean updated = permissionState.updateFlags(flagMask, flagValues);
- if (updated) {
- final int newFlags = permissionState.getFlags();
- if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
- && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- mPermissionReviewRequired = true;
- } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
- && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
- if (mPermissionReviewRequired && !hasPermissionRequiringReview()) {
- mPermissionReviewRequired = false;
- }
- }
- }
- return updated;
- }
- }
-
- private boolean hasPermissionRequiringReview() {
- synchronized (mLock) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- final PermissionState permission = mPermissions.valueAt(i);
- if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- return true;
- }
- }
- }
- return false;
- }
-
- public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
- synchronized (mLock) {
- if (mPermissions == null) {
- return false;
- }
- boolean changed = false;
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionState permissionState = mPermissions.valueAt(i);
- changed |= permissionState.updateFlags(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.
+ * Gets the state for a permission or null if none.
*
- * @param userId The device user id.
- * @return The gids for the device user.
- */
- @NonNull
- public int[] computeGids(@UserIdInt int userId) {
- int[] gids = mGlobalGids;
-
- synchronized (mLock) {
- if (mPermissions != null) {
- final int permissionCount = mPermissions.size();
- for (int i = 0; i < permissionCount; i++) {
- PermissionState permissionState = mPermissions.valueAt(i);
- if (!permissionState.isGranted()) {
- continue;
- }
- final int[] permGids = permissionState.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.
- */
- @NonNull
- public int[] computeGids(@NonNull 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 = false;
- mPermissionReviewRequired = false;
- }
-
- /**
- * Gets the state for a permission or null if no such.
- *
- * @param name The permission name.
- * @return The permission state.
+ * @param name the permission name.
+ * @return the permission state.
*/
@Nullable
public PermissionState getPermissionState(@NonNull String name) {
@@ -425,9 +170,9 @@
}
/**
- * Gets all permission states.
+ * Get all permission states.
*
- * @return The permission states or an empty set.
+ * @return the permission states
*/
@NonNull
public List<PermissionState> getPermissionStates() {
@@ -458,15 +203,56 @@
}
/**
+ * Get whether a permission is granted.
+ *
+ * @param name the permission name
+ * @return whether the permission is granted
+ */
+ public boolean isPermissionGranted(@NonNull String name) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ PermissionState permissionState = mPermissions.get(name);
+ return permissionState != null && permissionState.isGranted();
+ }
+ }
+
+ /**
+ * Get all the granted permissions.
+ *
+ * @return the granted permissions
+ */
+ @NonNull
+ public Set<String> getGrantedPermissions() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptySet();
+ }
+
+ Set<String> permissions = new ArraySet<>(mPermissions.size());
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+
+ if (permissionState.isGranted()) {
+ permissions.add(permissionState.getName());
+ }
+ }
+ return permissions;
+ }
+ }
+
+ /**
* Grant a permission.
*
- * @param permission The permission to grant.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * @param permission the permission to grantt
+ * @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 grantPermission(@NonNull BasePermission permission) {
- if (hasPermission(permission.getName())) {
+ if (isPermissionGranted(permission.getName())) {
return PERMISSION_OPERATION_SUCCESS;
}
@@ -483,20 +269,20 @@
/**
* Revoke a permission.
*
- * @param permission The permission to revoke.
- * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+ * @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 revokePermission(@NonNull BasePermission permission) {
- final String permissionName = permission.getName();
- if (!hasPermission(permissionName)) {
+ final String name = permission.getName();
+ if (!isPermissionGranted(name)) {
return PERMISSION_OPERATION_SUCCESS;
}
PermissionState permissionState;
synchronized (mLock) {
- permissionState = mPermissions.get(permissionName);
+ permissionState = mPermissions.get(name);
}
if (!permissionState.revoke()) {
@@ -504,34 +290,88 @@
}
if (permissionState.isDefault()) {
- ensureNoPermissionState(permissionName);
+ ensureNoPermissionState(name);
}
return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED
: 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);
- }
+ /**
+ * Get the flags for a permission.
+ *
+ * @param name the permission name.
+ * @return the permission flags
+ */
+ public int getPermissionFlags(@NonNull String name) {
+ final PermissionState permissionState = getPermissionState(name);
+ if (permissionState == null) {
+ return 0;
}
- return current;
+ return permissionState.getFlags();
+ }
+
+ /**
+ * Update the flags for a permission.
+ *
+ * @param permission the permission name
+ * @param flagMask the mask for the flags
+ * @param flagValues the new values for the masked flags
+ * @return whether the permission flags changed
+ */
+ public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask,
+ int flagValues) {
+ if (flagMask == 0) {
+ return false;
+ }
+
+ synchronized (mLock) {
+ final PermissionState permissionState = ensurePermissionState(permission);
+ final int oldFlags = permissionState.getFlags();
+
+ final boolean updated = permissionState.updateFlags(flagMask, flagValues);
+ if (updated) {
+ final int newFlags = permissionState.getFlags();
+ if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ mPermissionReviewRequired = true;
+ } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
+ && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+ if (mPermissionReviewRequired && !hasPermissionRequiringReview()) {
+ mPermissionReviewRequired = false;
+ }
+ }
+ }
+ return updated;
+ }
+ }
+
+ public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return false;
+ }
+ boolean changed = false;
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final PermissionState permissionState = mPermissions.valueAt(i);
+ changed |= permissionState.updateFlags(flagMask, flagValues);
+ }
+ return changed;
+ }
}
@NonNull
private PermissionState ensurePermissionState(@NonNull BasePermission permission) {
- final String permissionName = permission.getName();
+ final String name = permission.getName();
synchronized (mLock) {
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
}
- PermissionState permissionState = mPermissions.get(permissionName);
+ PermissionState permissionState = mPermissions.get(name);
if (permissionState == null) {
permissionState = new PermissionState(permission);
- mPermissions.put(permissionName, permissionState);
+ mPermissions.put(name, permissionState);
}
return permissionState;
}
@@ -548,4 +388,103 @@
}
}
}
+
+ public boolean isPermissionReviewRequired() {
+ return mPermissionReviewRequired;
+ }
+
+ private boolean hasPermissionRequiringReview() {
+ synchronized (mLock) {
+ final int permissionsSize = mPermissions.size();
+ for (int i = 0; i < permissionsSize; i++) {
+ final PermissionState permission = mPermissions.valueAt(i);
+ if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Gets the global gids, applicable to all users.
+ */
+ @NonNull
+ public int[] getGlobalGids() {
+ return mGlobalGids;
+ }
+
+ /**
+ * Sets the global gids, applicable to all users.
+ *
+ * @param globalGids The global gids.
+ */
+ public void setGlobalGids(@NonNull int[] globalGids) {
+ if (!ArrayUtils.isEmpty(globalGids)) {
+ mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+ } else {
+ mGlobalGids = NO_GIDS;
+ }
+ }
+
+ /**
+ * Compute the Linux GIDs from the permissions granted to a user.
+ *
+ * @param userId the user ID
+ * @return the GIDs for the user
+ */
+ @NonNull
+ public int[] computeGids(@UserIdInt int userId) {
+ int[] gids = mGlobalGids;
+
+ synchronized (mLock) {
+ if (mPermissions != null) {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ PermissionState permissionState = mPermissions.valueAt(i);
+ if (!permissionState.isGranted()) {
+ continue;
+ }
+ final int[] permGids = permissionState.computeGids(userId);
+ if (permGids != NO_GIDS) {
+ gids = appendInts(gids, permGids);
+ }
+ }
+ }
+ }
+
+ return gids;
+ }
+
+ /**
+ * Compute the Linux GIDs from the permissions granted to specified users.
+ *
+ * @param userIds the user IDs
+ * @return the GIDs for the user
+ */
+ @NonNull
+ public int[] computeGids(@NonNull int[] userIds) {
+ int[] gids = mGlobalGids;
+
+ for (final int userId : userIds) {
+ final int[] userGids = computeGids(userId);
+ gids = appendInts(gids, userGids);
+ }
+
+ return gids;
+ }
+
+ // 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;
+ }
+
+ static void invalidateCache() {
+ PackageManager.invalidatePackageInfoCache();
+ }
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 35b1449..df283e2 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1552,6 +1552,17 @@
startActivityAsUser(intent, UserHandle.CURRENT);
}
+ private void toggleNotificationPanel() {
+ IStatusBarService statusBarService = getStatusBarService();
+ if (statusBarService != null) {
+ try {
+ statusBarService.togglePanel();
+ } catch (RemoteException e) {
+ // do nothing.
+ }
+ }
+ }
+
private void showPictureInPictureMenu(KeyEvent event) {
if (DEBUG_INPUT) Log.d(TAG, "showPictureInPictureMenu event=" + event);
mHandler.removeMessages(MSG_SHOW_PICTURE_IN_PICTURE_MENU);
@@ -1696,14 +1707,7 @@
launchAssistAction(null, deviceId);
break;
case LONG_PRESS_HOME_NOTIFICATION_PANEL:
- IStatusBarService statusBarService = getStatusBarService();
- if (statusBarService != null) {
- try {
- statusBarService.togglePanel();
- } catch (RemoteException e) {
- // do nothing.
- }
- }
+ toggleNotificationPanel();
break;
default:
Log.w(TAG, "Undefined long press on home behavior: "
@@ -2807,6 +2811,11 @@
msg.sendToTarget();
}
return -1;
+ } else if (keyCode == KeyEvent.KEYCODE_NOTIFICATION) {
+ if (!down) {
+ toggleNotificationPanel();
+ }
+ return -1;
}
// Toggle Caps Lock on META-ALT.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6044ee1..60da8e5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -5408,6 +5408,22 @@
}
@Override // Binder call
+ public boolean isAmbientDisplaySuppressedForTokenByApp(@NonNull String token, int appUid) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_DREAM_STATE, null);
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_DREAM_SUPPRESSION, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isAmbientDisplayAvailable()
+ && mAmbientDisplaySuppressionController.isSuppressed(token, appUid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public boolean isAmbientDisplaySuppressed() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_DREAM_STATE, null);
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 8ccbbdd..cfceaabf 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -22,7 +22,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -33,6 +35,7 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -51,6 +54,7 @@
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSession;
import android.media.tv.ITvInputSessionCallback;
+import android.media.tv.TvChannelInfo;
import android.media.tv.TvContentRating;
import android.media.tv.TvContentRatingSystemInfo;
import android.media.tv.TvContract;
@@ -78,6 +82,7 @@
import android.view.InputChannel;
import android.view.Surface;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.DumpUtils;
@@ -108,6 +113,9 @@
private static final boolean DEBUG = false;
private static final String TAG = "TvInputManagerService";
private static final String DVB_DIRECTORY = "/dev/dvb";
+ private static final int APP_TAG_SELF = TvChannelInfo.APP_TAG_SELF;
+ private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS =
+ "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS";
// There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d,
// another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the
@@ -136,6 +144,8 @@
private final WatchLogHandler mWatchLogHandler;
+ private final ActivityManager mActivityManager;
+
public TvInputManagerService(Context context) {
super(context);
@@ -144,6 +154,9 @@
IoThread.get().getLooper());
mTvInputHardwareManager = new TvInputHardwareManager(context, new HardwareListener());
+ mActivityManager =
+ (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE);
+
synchronized (mLock) {
getOrCreateUserStateLocked(mCurrentUserId);
}
@@ -686,14 +699,18 @@
SessionState sessionState = null;
try {
sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+ UserState userState = getOrCreateUserStateLocked(userId);
if (sessionState.session != null) {
- UserState userState = getOrCreateUserStateLocked(userId);
if (sessionToken == userState.mainSessionToken) {
setMainLocked(sessionToken, false, callingUid, userId);
}
sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
sessionState.session.release();
}
+ sessionState.isCurrent = false;
+ sessionState.currentChannel = null;
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
} catch (RemoteException | SessionNotFoundException e) {
Slog.e(TAG, "error in releaseSession", e);
} finally {
@@ -834,6 +851,22 @@
}
}
+ private void notifyCurrentChannelInfosUpdatedLocked(
+ UserState userState, List<TvChannelInfo> infos) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyCurrentChannelInfosUpdatedLocked");
+ }
+ int n = userState.mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; ++i) {
+ try {
+ userState.mCallbacks.getBroadcastItem(i).onCurrentTvChannelInfosUpdated(infos);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "failed to report updated current channel infos to callback", e);
+ }
+ }
+ userState.mCallbacks.finishBroadcast();
+ }
+
private void updateTvInputInfoLocked(UserState userState, TvInputInfo inputInfo) {
if (DEBUG) {
Slog.d(TAG, "updateTvInputInfoLocked(inputInfo=" + inputInfo + ")");
@@ -1394,13 +1427,19 @@
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(
channelUri, params);
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+ if (sessionState != null) {
+ sessionState.isCurrent = true;
+ sessionState.currentChannel = channelUri;
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
+ }
if (TvContract.isChannelUriForPassthroughInput(channelUri)) {
// Do not log the watch history for passthrough inputs.
return;
}
- UserState userState = getOrCreateUserStateLocked(resolvedUserId);
- SessionState sessionState = userState.sessionStateMap.get(sessionToken);
if (sessionState.isRecordingSession) {
return;
}
@@ -2049,6 +2088,21 @@
return clientPid;
}
+ @Override
+ public List<TvChannelInfo> getCurrentTvChannelInfos(@UserIdInt int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "getTvCurrentChannelInfos");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ return getCurrentTvChannelInfosInternalLocked(userState);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
/**
* Add a hardware device in the TvInputHardwareManager for CTS testing
* purpose.
@@ -2219,6 +2273,69 @@
}
}
+ private List<TvChannelInfo> getCurrentTvChannelInfosInternalLocked(UserState userState) {
+ List<TvChannelInfo> channelInfos = new ArrayList<>();
+ boolean watchedProgramsAccess = hasAccessWatchedProgramsPermission();
+ for (SessionState state : userState.sessionStateMap.values()) {
+ if (state.isCurrent) {
+ Integer appTag;
+ int appType;
+ if (state.callingUid == Binder.getCallingUid()) {
+ appTag = APP_TAG_SELF;
+ appType = TvChannelInfo.APP_TYPE_SELF;
+ } else {
+ appTag = userState.mAppTagMap.get(state.callingUid);
+ if (appTag == null) {
+ appTag = userState.mNextAppTag++;
+ userState.mAppTagMap.put(state.callingUid, appTag);
+ }
+ appType = isSystemApp(state.componentName.getPackageName())
+ ? TvChannelInfo.APP_TYPE_SYSTEM
+ : TvChannelInfo.APP_TYPE_NON_SYSTEM;
+ }
+ channelInfos.add(new TvChannelInfo(
+ state.inputId,
+ watchedProgramsAccess ? state.currentChannel : null,
+ state.isRecordingSession,
+ isForeground(state.callingPid),
+ appType,
+ appTag));
+ }
+ }
+ return channelInfos;
+ }
+
+ private boolean isForeground(int pid) {
+ if (mActivityManager == null) {
+ return false;
+ }
+ List<RunningAppProcessInfo> appProcesses = mActivityManager.getRunningAppProcesses();
+ if (appProcesses == null) {
+ return false;
+ }
+ for (RunningAppProcessInfo appProcess : appProcesses) {
+ if (appProcess.pid == pid
+ && appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean hasAccessWatchedProgramsPermission() {
+ return mContext.checkCallingPermission(PERMISSION_ACCESS_WATCHED_PROGRAMS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private boolean isSystemApp(String pkg) {
+ try {
+ return (mContext.getPackageManager().getApplicationInfo(pkg, 0).flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
private static final class UserState {
// A mapping from the TV input id to its TvInputState.
private Map<String, TvInputState> inputMap = new HashMap<>();
@@ -2250,6 +2367,11 @@
// service.
private final PersistentDataStore persistentDataStore;
+ @GuardedBy("mLock")
+ private final Map<Integer, Integer> mAppTagMap = new HashMap<>();
+ @GuardedBy("mLock")
+ private int mNextAppTag = 1;
+
private UserState(Context context, int userId) {
persistentDataStore = new PersistentDataStore(context, userId);
}
@@ -2336,6 +2458,9 @@
// Not null if this session represents an external device connected to a hardware TV input.
private IBinder hardwareSessionToken;
+ private boolean isCurrent = false;
+ private Uri currentChannel = null;
+
private SessionState(IBinder sessionToken, String inputId, ComponentName componentName,
boolean isRecordingSession, ITvInputClient client, int seq, int callingUid,
int callingPid, int userId, String sessionId) {
@@ -2584,6 +2709,11 @@
if (mSessionState.session == null || mSessionState.client == null) {
return;
}
+ mSessionState.isCurrent = true;
+ mSessionState.currentChannel = channelUri;
+ UserState userState = getOrCreateUserStateLocked(mSessionState.userId);
+ notifyCurrentChannelInfosUpdatedLocked(
+ userState, getCurrentTvChannelInfosInternalLocked(userState));
try {
// TODO: Consider adding this channel change in the watch log. When we do
// that, how we can protect the watch log from malicious tv inputs should
diff --git a/services/core/java/com/android/server/utils/XmlName.java b/services/core/java/com/android/server/utils/XmlName.java
new file mode 100644
index 0000000..c0e22da
--- /dev/null
+++ b/services/core/java/com/android/server/utils/XmlName.java
@@ -0,0 +1,31 @@
+/*
+ * 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.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specify the XML name for the annotated field or persistence class.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target({ ElementType.FIELD, ElementType.TYPE })
+public @interface XmlName {
+ String value();
+}
diff --git a/services/core/java/com/android/server/utils/XmlPersistence.java b/services/core/java/com/android/server/utils/XmlPersistence.java
new file mode 100644
index 0000000..8900a9d
--- /dev/null
+++ b/services/core/java/com/android/server/utils/XmlPersistence.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Generate XML persistence for the annotated class.
+ */
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface XmlPersistence {
+ /**
+ * Name for the generated XML persistence class.
+ * <p>
+ * The name defaults to the target class name appended with {@code Persistence} if unspecified.
+ */
+ String value();
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6792430..1cb610f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4465,6 +4465,9 @@
}
detachChildren();
}
+ if (app != null) {
+ app.invalidateOomScoreReferenceState(false /* computeNow */);
+ }
switch (state) {
case RESUMED:
@@ -7864,7 +7867,7 @@
InsetUtils.addInsets(insets, getLetterboxInsets());
return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
record.mAdapter.mCapturedLeash, !fillsParent(),
- mainWindow.mWinAnimator.mLastClipRect, insets,
+ new Rect(), insets,
getPrefixOrderIndex(), record.mAdapter.mPosition, record.mAdapter.mLocalBounds,
record.mAdapter.mStackBounds, task.getWindowConfiguration(),
false /*isNotInRecents*/,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 2dc22ec..e65be41 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -499,9 +499,6 @@
/** @return the process for the top-most resumed activity in the system. */
public abstract WindowProcessController getTopApp();
- /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
- public abstract void rankTaskLayersIfNeeded();
-
/** Destroy all activities. */
public abstract void scheduleDestroyAllActivities(String reason);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 15669cb..0402140 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -666,7 +666,7 @@
*/
CompatModePackages mCompatModePackages;
- private FontScaleSettingObserver mFontScaleSettingObserver;
+ private SettingObserver mSettingsObserver;
WindowOrganizerController mWindowOrganizerController;
TaskOrganizerController mTaskOrganizerController;
@@ -676,16 +676,19 @@
private int mDeviceOwnerUid = Process.INVALID_UID;
- private final class FontScaleSettingObserver extends ContentObserver {
+ private final class SettingObserver extends ContentObserver {
private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
+ private final Uri mForceBoldTextUri = Settings.Secure.getUriFor(
+ Settings.Secure.FORCE_BOLD_TEXT);
- public FontScaleSettingObserver() {
+ SettingObserver() {
super(mH);
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(mForceBoldTextUri, false, this, UserHandle.USER_ALL);
}
@Override
@@ -698,6 +701,8 @@
synchronized (mGlobalLock) {
updateShouldShowDialogsLocked(getGlobalConfiguration());
}
+ } else if (mForceBoldTextUri.equals(uri)) {
+ updateForceBoldTextIfNeeded(userId);
}
}
}
@@ -757,7 +762,7 @@
}
public void installSystemProviders() {
- mFontScaleSettingObserver = new FontScaleSettingObserver();
+ mSettingsObserver = new SettingObserver();
}
public void retrieveSettings(ContentResolver resolver) {
@@ -5362,6 +5367,20 @@
}
}
+ private void updateForceBoldTextIfNeeded(@UserIdInt int userId) {
+ final int forceBoldTextConfig = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FORCE_BOLD_TEXT, Configuration.FORCE_BOLD_TEXT_UNDEFINED, userId);
+ synchronized (mGlobalLock) {
+ if (getGlobalConfiguration().forceBoldText == forceBoldTextConfig) {
+ return;
+ }
+ final Configuration configuration =
+ mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+ configuration.forceBoldText = forceBoldTextConfig;
+ updatePersistentConfiguration(configuration, userId);
+ }
+ }
+
// Actually is sleeping or shutting down or whatever else in the future
// is an inactive state.
boolean isSleepingOrShuttingDownLocked() {
@@ -7187,16 +7206,6 @@
}
}
- @HotPath(caller = HotPath.OOM_ADJUSTMENT)
- @Override
- public void rankTaskLayersIfNeeded() {
- synchronized (mGlobalLockWithoutBoost) {
- if (mRootWindowContainer != null) {
- mRootWindowContainer.rankTaskLayersIfNeeded();
- }
- }
- }
-
@Override
public void scheduleDestroyAllActivities(String reason) {
synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b3e69d4..1153c46 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -193,7 +193,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
-import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -552,11 +551,6 @@
private SurfaceControl mParentSurfaceControl;
private InputWindowHandle mPortalWindowHandle;
- // Last systemUiVisibility we received from status bar.
- private int mLastStatusBarVisibility = 0;
- // Last systemUiVisibility we dispatched to windows.
- private int mLastDispatchedSystemUiVisibility = 0;
-
/** Corner radius that windows should have in order to match the display. */
private final float mWindowCornerRadius;
@@ -2907,10 +2901,6 @@
pw.print(" mLastFocus="); pw.println(mLastFocus);
}
pw.print(" mFocusedApp="); pw.println(mFocusedApp);
- if (mLastStatusBarVisibility != 0) {
- pw.print(" mLastStatusBarVisibility=0x");
- pw.println(Integer.toHexString(mLastStatusBarVisibility));
- }
if (mFixedRotationLaunchingApp != null) {
pw.println(" mFixedRotationLaunchingApp=" + mFixedRotationLaunchingApp);
}
@@ -3770,50 +3760,6 @@
return win != null;
}
- void hideTransientBars() {
- // TODO(b/118118435): Remove this after migration
- final int transientFlags = View.STATUS_BAR_TRANSIENT | View.NAVIGATION_BAR_TRANSIENT;
- statusBarVisibilityChanged(mLastStatusBarVisibility & ~transientFlags);
-
- getInsetsPolicy().hideTransient();
- }
-
- void statusBarVisibilityChanged(int visibility) {
- mLastStatusBarVisibility = visibility;
- updateStatusBarVisibilityLocked(visibility);
- }
-
- private boolean updateStatusBarVisibilityLocked(int visibility) {
- if (mLastDispatchedSystemUiVisibility == visibility) {
- return false;
- }
- final int globalDiff = (visibility ^ mLastDispatchedSystemUiVisibility)
- // We are only interested in differences of one of the
- // clearable flags...
- & View.SYSTEM_UI_CLEARABLE_FLAGS
- // ...if it has actually been cleared.
- & ~visibility;
-
- mLastDispatchedSystemUiVisibility = visibility;
- if (isDefaultDisplay) {
- mWmService.mInputManager.setSystemUiVisibility(visibility);
- }
- updateSystemUiVisibility(visibility, globalDiff);
- return true;
- }
-
- void updateSystemUiVisibility(int visibility, int globalDiff) {
- forAllWindows(w -> {
- final int curValue = w.mSystemUiVisibility;
- final int diff = (curValue ^ visibility) & globalDiff;
- final int newValue = (curValue & ~diff) | (visibility & diff);
- if (newValue != curValue) {
- w.mSeq++;
- w.mSystemUiVisibility = newValue;
- }
- }, true /* traverseTopToBottom */);
- }
-
void onWindowFreezeTimeout() {
Slog.w(TAG_WM, "Window freeze timeout expired.");
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e54da9e..dce798e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -43,6 +43,7 @@
import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
@@ -156,6 +157,7 @@
import android.view.PointerIcon;
import android.view.Surface;
import android.view.View;
+import android.view.ViewDebug;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
@@ -1445,9 +1447,9 @@
boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout) {
- final int fl = PolicyControl.getWindowFlags(null, attrs);
+ final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
- final int sysUiVis = PolicyControl.getSystemUiVisibility(null, attrs);
+ final int sysUiVis = attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility;
final boolean layoutInScreen = (fl & FLAG_LAYOUT_IN_SCREEN) != 0;
final boolean layoutInScreenAndInsetDecor = layoutInScreen
@@ -2001,7 +2003,7 @@
final WindowManager.LayoutParams attrs = win.getAttrs();
final int type = attrs.type;
- final int fl = PolicyControl.getWindowFlags(win, attrs);
+ final int fl = attrs.flags;
final int pfl = attrs.privateFlags;
final int sim = attrs.softInputMode;
@@ -2243,7 +2245,7 @@
final boolean affectsSystemUi = win.canAffectSystemUiFlags();
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": affectsSystemUi=" + affectsSystemUi);
mService.mPolicy.applyKeyguardPolicyLw(win, imeTarget);
- final int fl = PolicyControl.getWindowFlags(win, attrs);
+ final int fl = attrs.flags;
if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi
&& attrs.type == TYPE_INPUT_METHOD) {
mForcingShowNavBar = true;
@@ -2418,8 +2420,7 @@
return false;
}
final LayoutParams attrs = mTopFullscreenOpaqueWindowState.getAttrs();
- final int fl = PolicyControl.getWindowFlags(null, attrs);
- final int sysui = PolicyControl.getSystemUiVisibility(null, attrs);
+ final int fl = attrs.flags;
final InsetsSource request = mTopFullscreenOpaqueWindowState.getRequestedInsetsState()
.peekSource(ITYPE_STATUS_BAR);
if (WindowManagerDebugConfig.DEBUG) {
@@ -2952,9 +2953,9 @@
mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
- final int fullscreenAppearance = updateLightStatusBarLw(0 /* vis */,
+ final int fullscreenAppearance = updateLightStatusBarLw(0 /* appearance */,
mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
- final int dockedAppearance = updateLightStatusBarLw(0 /* vis */,
+ final int dockedAppearance = updateLightStatusBarLw(0 /* appearance */,
mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
final boolean inSplitScreen =
mService.mRoot.getDefaultTaskDisplayArea().isSplitScreenModeActivated();
@@ -3025,6 +3026,10 @@
}
});
+ if (mDisplayContent.isDefaultDisplay) {
+ mService.mInputManager.setSystemUiLightsOut(
+ isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0);
+ }
return true;
}
@@ -3037,9 +3042,7 @@
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
// its light flag.
appearance &= ~APPEARANCE_LIGHT_STATUS_BARS;
- final int legacyAppearance = InsetsFlags.getAppearance(
- PolicyControl.getSystemUiVisibility(statusColorWin, null));
- appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance)
+ appearance |= statusColorWin.mAttrs.insetsFlags.appearance
& APPEARANCE_LIGHT_STATUS_BARS;
} else if (statusColorWin.isDimming()) {
// Otherwise if it's dimming, clear the light flag.
@@ -3062,8 +3065,8 @@
final boolean imeWindowCanNavColorWindow = imeWindow != null
&& imeWindow.isVisibleLw()
&& navBarPosition == NAV_BAR_BOTTOM
- && (PolicyControl.getWindowFlags(imeWindow, null)
- & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ && (imeWindow.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
if (opaque != null && opaqueOrDimming == opaque) {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect it
@@ -3085,7 +3088,7 @@
// The IME window and the dimming window are competing. Check if the dimming window can be
// IME target or not.
- if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
+ if (LayoutParams.mayUseInputMethod(opaqueOrDimming.mAttrs.flags)) {
// The IME window is above the dimming window.
return imeWindow;
} else {
@@ -3359,22 +3362,30 @@
pw.print(prefix); pw.print("mHdmiPlugged="); pw.println(mHdmiPlugged);
if (mLastDisableFlags != 0) {
pw.print(prefix); pw.print("mLastDisableFlags=0x");
- pw.print(Integer.toHexString(mLastDisableFlags));
+ pw.println(Integer.toHexString(mLastDisableFlags));
+ }
+ if (mLastAppearance != 0) {
+ pw.print(prefix); pw.print("mLastAppearance=");
+ pw.println(ViewDebug.flagsToString(InsetsFlags.class, "appearance", mLastAppearance));
+ }
+ if (mLastBehavior != 0) {
+ pw.print(prefix); pw.print("mLastBehavior=");
+ pw.println(ViewDebug.flagsToString(InsetsFlags.class, "behavior", mLastBehavior));
}
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
- pw.print(" mDreamingLockscreen="); pw.print(mDreamingLockscreen);
+ pw.print(" mDreamingLockscreen="); pw.println(mDreamingLockscreen);
if (mStatusBar != null) {
- pw.print(prefix); pw.print("mStatusBar="); pw.print(mStatusBar);
+ pw.print(prefix); pw.print("mStatusBar="); pw.println(mStatusBar);
}
if (mStatusBarAlt != null) {
- pw.print(prefix); pw.print("mStatusBarAlt="); pw.print(mStatusBarAlt);
+ pw.print(prefix); pw.print("mStatusBarAlt="); pw.println(mStatusBarAlt);
pw.print(prefix); pw.print("mStatusBarAltPosition=");
pw.println(mStatusBarAltPosition);
}
if (mNotificationShade != null) {
- pw.print(prefix); pw.print("mExpandedPanel="); pw.print(mNotificationShade);
+ pw.print(prefix); pw.print("mExpandedPanel="); pw.println(mNotificationShade);
}
- pw.print(" isKeyguardShowing="); pw.println(isKeyguardShowing());
+ pw.print(prefix); pw.print("isKeyguardShowing="); pw.println(isKeyguardShowing());
if (mNavigationBar != null) {
pw.print(prefix); pw.print("mNavigationBar="); pw.println(mNavigationBar);
pw.print(prefix); pw.print("mNavBarOpacityMode="); pw.println(mNavBarOpacityMode);
@@ -3415,9 +3426,9 @@
}
pw.print(prefix); pw.print("mTopIsFullscreen="); pw.println(mTopIsFullscreen);
pw.print(prefix); pw.print("mForceStatusBar="); pw.print(mForceStatusBar);
- pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars");
- pw.print(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
+ pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars=");
+ pw.println(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
pw.print(prefix); pw.println("Looper state:");
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index f0f3385..1d8cdf7 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -138,8 +138,9 @@
&& dcTarget.getParentWindow() == mImeTargetFromIme
&& dcTarget.mSubLayer > mImeTargetFromIme.getWindow().mSubLayer)
|| mImeTargetFromIme == mDisplayContent.getImeFallback()
+ || mImeTargetFromIme == mDisplayContent.mInputMethodInputTarget
|| controlTarget == mImeTargetFromIme
- && (mImeTargetFromIme.getWindow() == null
+ && (mImeTargetFromIme.getWindow() == null
|| !mImeTargetFromIme.getWindow().isClosing());
}
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 8b1a0c9..02dad39 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.os.Binder;
@@ -46,6 +47,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -134,11 +136,8 @@
boolean userSetupComplete, boolean navBarEmpty) {
mHandler.removeMessages(H.SHOW);
if (isImmersiveMode) {
- final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
- if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s sConfirmed=%s",
- disabled, sConfirmed));
- if (!disabled
- && (DEBUG_SHOW_EVERY_TIME || !sConfirmed)
+ if (DEBUG) Slog.d(TAG, "immersiveModeChanged() sConfirmed=" + sConfirmed);
+ if ((DEBUG_SHOW_EVERY_TIME || !sConfirmed)
&& userSetupComplete
&& !mVrModeEnabled
&& !navBarEmpty
@@ -339,6 +338,13 @@
public boolean onTouchEvent(MotionEvent motion) {
return true;
}
+
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ // we will be hiding the nav bar, so layout as if it's already hidden
+ return new WindowInsets.Builder(insets).setInsets(
+ Type.systemBars(), Insets.NONE).build();
+ }
}
/**
@@ -359,10 +365,6 @@
mClingWindow = new ClingWindowView(mContext, mConfirm);
- // we will be hiding the nav bar, so layout as if it's already hidden
- mClingWindow.setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-
// show the confirmation
WindowManager.LayoutParams lp = getClingWindowLayoutParams();
getWindowManager().addView(mClingWindow, lp);
diff --git a/services/core/java/com/android/server/wm/PolicyControl.java b/services/core/java/com/android/server/wm/PolicyControl.java
deleted file mode 100644
index 61b6e0b..0000000
--- a/services/core/java/com/android/server/wm/PolicyControl.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.ArraySet;
-import android.util.Slog;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-/**
- * Runtime adjustments applied to the global window policy.
- *
- * This includes forcing immersive mode behavior for one or both system bars (based on a package
- * list) and permanently disabling immersive mode confirmations for specific packages.
- *
- * Control by setting {@link Settings.Global#POLICY_CONTROL} to one or more name-value pairs.
- * e.g.
- * to force immersive mode everywhere:
- * "immersive.full=*"
- * to force transient status for all apps except a specific package:
- * "immersive.status=apps,-com.package"
- * to disable the immersive mode confirmations for specific packages:
- * "immersive.preconfirms=com.package.one,com.package.two"
- *
- * Separate multiple name-value pairs with ':'
- * e.g. "immersive.status=apps:immersive.preconfirms=*"
- */
-class PolicyControl {
- private static final String TAG = "PolicyControl";
- private static final boolean DEBUG = false;
-
- @VisibleForTesting
- static final String NAME_IMMERSIVE_FULL = "immersive.full";
- private static final String NAME_IMMERSIVE_STATUS = "immersive.status";
- private static final String NAME_IMMERSIVE_NAVIGATION = "immersive.navigation";
- private static final String NAME_IMMERSIVE_PRECONFIRMATIONS = "immersive.preconfirms";
-
- private static String sSettingValue;
- private static Filter sImmersivePreconfirmationsFilter;
- private static Filter sImmersiveStatusFilter;
- private static Filter sImmersiveNavigationFilter;
-
- static int getSystemUiVisibility(WindowState win, LayoutParams attrs) {
- attrs = attrs != null ? attrs : win.getAttrs();
- int vis = win != null ? win.getSystemUiVisibility()
- : (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility);
- if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
- vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_FULLSCREEN;
- if (attrs.isFullscreen()) {
- vis |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
- }
- vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.STATUS_BAR_TRANSLUCENT);
- }
- if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) {
- vis |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
- if (attrs.isFullscreen()) {
- vis |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- }
- vis &= ~(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.NAVIGATION_BAR_TRANSLUCENT);
- }
- return vis;
- }
-
- static int getWindowFlags(WindowState win, LayoutParams attrs) {
- attrs = attrs != null ? attrs : win.getAttrs();
- int flags = attrs.flags;
- if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
- flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
- flags &= ~(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
- | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
- }
- if (sImmersiveNavigationFilter != null && sImmersiveNavigationFilter.matches(attrs)) {
- flags &= ~WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
- }
- return flags;
- }
-
- static int adjustClearableFlags(WindowState win, int clearableFlags) {
- final LayoutParams attrs = win != null ? win.getAttrs() : null;
- if (sImmersiveStatusFilter != null && sImmersiveStatusFilter.matches(attrs)) {
- clearableFlags &= ~View.SYSTEM_UI_FLAG_FULLSCREEN;
- }
- return clearableFlags;
- }
-
- static boolean disableImmersiveConfirmation(String pkg) {
- return (sImmersivePreconfirmationsFilter != null
- && sImmersivePreconfirmationsFilter.matches(pkg))
- || ActivityManager.isRunningInTestHarness();
- }
-
- static boolean reloadFromSetting(Context context) {
- if (DEBUG) Slog.d(TAG, "reloadFromSetting()");
- String value = null;
- try {
- value = Settings.Global.getStringForUser(context.getContentResolver(),
- Settings.Global.POLICY_CONTROL,
- UserHandle.USER_CURRENT);
- if (sSettingValue == value || sSettingValue != null && sSettingValue.equals(value)) {
- return false;
- }
- setFilters(value);
- sSettingValue = value;
- } catch (Throwable t) {
- Slog.w(TAG, "Error loading policy control, value=" + value, t);
- return false;
- }
- return true;
- }
-
- static void dump(String prefix, PrintWriter pw) {
- dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw);
- dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw);
- dump("sImmersivePreconfirmationsFilter", sImmersivePreconfirmationsFilter, prefix, pw);
- }
-
- private static void dump(String name, Filter filter, String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("PolicyControl."); pw.print(name); pw.print('=');
- if (filter == null) {
- pw.println("null");
- } else {
- filter.dump(pw); pw.println();
- }
- }
-
- @VisibleForTesting
- static void setFilters(String value) {
- if (DEBUG) Slog.d(TAG, "setFilters: " + value);
- sImmersiveStatusFilter = null;
- sImmersiveNavigationFilter = null;
- sImmersivePreconfirmationsFilter = null;
- if (value != null) {
- String[] nvps = value.split(":");
- for (String nvp : nvps) {
- int i = nvp.indexOf('=');
- if (i == -1) continue;
- String n = nvp.substring(0, i);
- String v = nvp.substring(i + 1);
- if (n.equals(NAME_IMMERSIVE_FULL)) {
- Filter f = Filter.parse(v);
- sImmersiveStatusFilter = sImmersiveNavigationFilter = f;
- if (sImmersivePreconfirmationsFilter == null) {
- sImmersivePreconfirmationsFilter = f;
- }
- } else if (n.equals(NAME_IMMERSIVE_STATUS)) {
- Filter f = Filter.parse(v);
- sImmersiveStatusFilter = f;
- } else if (n.equals(NAME_IMMERSIVE_NAVIGATION)) {
- Filter f = Filter.parse(v);
- sImmersiveNavigationFilter = f;
- if (sImmersivePreconfirmationsFilter == null) {
- sImmersivePreconfirmationsFilter = f;
- }
- } else if (n.equals(NAME_IMMERSIVE_PRECONFIRMATIONS)) {
- Filter f = Filter.parse(v);
- sImmersivePreconfirmationsFilter = f;
- }
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "immersiveStatusFilter: " + sImmersiveStatusFilter);
- Slog.d(TAG, "immersiveNavigationFilter: " + sImmersiveNavigationFilter);
- Slog.d(TAG, "immersivePreconfirmationsFilter: " + sImmersivePreconfirmationsFilter);
- }
- }
-
- private static class Filter {
- private static final String ALL = "*";
- private static final String APPS = "apps";
-
- private final ArraySet<String> mAllowlist;
- private final ArraySet<String> mDenylist;
-
- private Filter(ArraySet<String> allowlist, ArraySet<String> denylist) {
- mAllowlist = allowlist;
- mDenylist = denylist;
- }
-
- boolean matches(LayoutParams attrs) {
- if (attrs == null) return false;
- boolean isApp = attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
- if (isApp && mDenylist.contains(APPS)) return false;
- if (onDenylist(attrs.packageName)) return false;
- if (isApp && mAllowlist.contains(APPS)) return true;
- return onAllowlist(attrs.packageName);
- }
-
- boolean matches(String packageName) {
- return !onDenylist(packageName) && onAllowlist(packageName);
- }
-
- private boolean onDenylist(String packageName) {
- return mDenylist.contains(packageName) || mDenylist.contains(ALL);
- }
-
- private boolean onAllowlist(String packageName) {
- return mAllowlist.contains(ALL) || mAllowlist.contains(packageName);
- }
-
- void dump(PrintWriter pw) {
- pw.print("Filter[");
- dump("allowlist", mAllowlist, pw); pw.print(',');
- dump("denylist", mDenylist, pw); pw.print(']');
- }
-
- private void dump(String name, ArraySet<String> set, PrintWriter pw) {
- pw.print(name); pw.print("=(");
- final int n = set.size();
- for (int i = 0; i < n; i++) {
- if (i > 0) pw.print(',');
- pw.print(set.valueAt(i));
- }
- pw.print(')');
- }
-
- @Override
- public String toString() {
- StringWriter sw = new StringWriter();
- dump(new PrintWriter(sw, true));
- return sw.toString();
- }
-
- // value = comma-delimited list of tokens, where token = (package name|apps|*)
- // e.g. "com.package1", or "apps, com.android.keyguard" or "*"
- static Filter parse(String value) {
- if (value == null) return null;
- ArraySet<String> allowlist = new ArraySet<String>();
- ArraySet<String> denylist = new ArraySet<String>();
- for (String token : value.split(",")) {
- token = token.trim();
- if (token.startsWith("-") && token.length() > 1) {
- token = token.substring(1);
- denylist.add(token);
- } else {
- allowlist.add(token);
- }
- }
- return new Filter(allowlist, denylist);
- }
- }
-}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index b50cb4c..42a20ff 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -929,7 +929,7 @@
? MODE_OPENING
: MODE_CLOSING;
mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
- !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
+ !topApp.fillsParent(), new Rect(),
insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
mLocalBounds, mBounds, mTask.getWindowConfiguration(),
mIsRecentTaskInvisible, null, null);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 71ecf72..8b52b58 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -276,6 +276,8 @@
// Whether tasks have moved and we need to rank the tasks before next OOM scoring
private boolean mTaskLayersChanged = true;
private int mTmpTaskLayerRank;
+ private final ArraySet<WindowProcessController> mTmpTaskLayerChangedProcs = new ArraySet<>();
+ private final LockedScheduler mRankTaskLayersScheduler;
private boolean mTmpBoolean;
private RemoteException mTmpRemoteException;
@@ -450,6 +452,12 @@
mStackSupervisor = mService.mStackSupervisor;
mStackSupervisor.mRootWindowContainer = this;
mDisplayOffTokenAcquirer = mService.new SleepTokenAcquirerImpl("Display-off");
+ mRankTaskLayersScheduler = new LockedScheduler(mService) {
+ @Override
+ public void execute() {
+ rankTaskLayersIfNeeded();
+ }
+ };
}
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
@@ -2698,27 +2706,39 @@
void invalidateTaskLayers() {
mTaskLayersChanged = true;
+ mRankTaskLayersScheduler.scheduleIfNeeded();
}
+ /** Generate oom-score-adjustment rank for all tasks in the system based on z-order. */
void rankTaskLayersIfNeeded() {
if (!mTaskLayersChanged) {
return;
}
mTaskLayersChanged = false;
mTmpTaskLayerRank = 0;
- final PooledConsumer c = PooledLambda.obtainConsumer(
- RootWindowContainer::rankTaskLayerForActivity, this,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(c);
- c.recycle();
- }
+ // Only rank for leaf tasks because the score of activity is based on immediate parent.
+ forAllLeafTasks(task -> {
+ final int oldRank = task.mLayerRank;
+ final ActivityRecord r = task.topRunningActivityLocked();
+ if (r != null && r.mVisibleRequested) {
+ task.mLayerRank = ++mTmpTaskLayerRank;
+ } else {
+ task.mLayerRank = Task.LAYER_RANK_INVISIBLE;
+ }
+ if (task.mLayerRank != oldRank) {
+ task.forAllActivities(activity -> {
+ if (activity.hasProcess()) {
+ mTmpTaskLayerChangedProcs.add(activity.app);
+ }
+ });
+ }
+ }, true /* traverseTopToBottom */);
- private void rankTaskLayerForActivity(ActivityRecord r) {
- if (r.canBeTopRunning() && r.mVisibleRequested) {
- r.getTask().mLayerRank = ++mTmpTaskLayerRank;
- } else {
- r.getTask().mLayerRank = -1;
+ for (int i = mTmpTaskLayerChangedProcs.size() - 1; i >= 0; i--) {
+ mTmpTaskLayerChangedProcs.valueAt(i).invalidateOomScoreReferenceState(
+ true /* computeNow */);
}
+ mTmpTaskLayerChangedProcs.clear();
}
void clearOtherAppTimeTrackers(AppTimeTracker except) {
@@ -3680,4 +3700,34 @@
+ ", acquire at " + TimeUtils.formatUptime(mAcquireTime) + "}";
}
}
+
+ /**
+ * Helper class to schedule the runnable if it hasn't scheduled on display thread inside window
+ * manager lock.
+ */
+ abstract static class LockedScheduler implements Runnable {
+ private final ActivityTaskManagerService mService;
+ private boolean mScheduled;
+
+ LockedScheduler(ActivityTaskManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public void run() {
+ synchronized (mService.mGlobalLock) {
+ mScheduled = false;
+ execute();
+ }
+ }
+
+ abstract void execute();
+
+ void scheduleIfNeeded() {
+ if (!mScheduled) {
+ mService.mH.post(this);
+ mScheduled = true;
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 5f2113a..9ff99f5 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -157,33 +157,33 @@
}
@Override
- public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
+ return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
outInsetsState, outActiveControls, UserHandle.getUserId(mUid));
}
@Override
- public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
+ return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame,
outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,
outInsetsState, outActiveControls, userId);
}
@Override
- public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
InsetsState outInsetsState) {
- return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
+ return mService.addWindow(this, window, attrs, viewVisibility, displayId,
new Rect() /* outFrame */, outContentInsets, outStableInsets,
new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */,
outInsetsState, mDummyControls, UserHandle.getUserId(mUid));
@@ -200,7 +200,7 @@
}
@Override
- public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
+ public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
@@ -209,7 +209,7 @@
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
- int res = mService.relayoutWindow(this, window, seq, attrs,
+ int res = mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
outActiveControls, outSurfaceSize, outBLASTSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ce602de..9be4ace 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -464,9 +464,10 @@
int mMinWidth;
int mMinHeight;
+ static final int LAYER_RANK_INVISIBLE = -1;
// Ranking (from top) of this task among all visible tasks. (-1 means it's not visible)
// This number will be assigned when we evaluate OOM scores for all visible tasks.
- int mLayerRank = -1;
+ int mLayerRank = LAYER_RANK_INVISIBLE;
/** Helper object used for updating override configuration. */
private Configuration mTmpConfig = new Configuration();
@@ -1539,7 +1540,6 @@
if (isPersistable) {
mLastTimeMoved = System.currentTimeMillis();
}
- mRootWindowContainer.invalidateTaskLayers();
}
// Close up recents linked list.
@@ -7452,6 +7452,10 @@
if (!mChildren.contains(child)) {
return;
}
+ if (child.asTask() != null) {
+ // Non-root task position changed.
+ mRootWindowContainer.invalidateTaskLayers();
+ }
final boolean isTop = getTopChild() == child;
if (isTop) {
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 76bd6ce..55e6a78 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -441,6 +441,12 @@
}
@Override
+ void onChildPositionChanged(WindowContainer child) {
+ super.onChildPositionChanged(child);
+ mRootWindowContainer.invalidateTaskLayers();
+ }
+
+ @Override
boolean forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean> callback,
boolean traverseTopToBottom) {
return callback.apply(this);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index cccda3a..6904740 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -241,7 +241,7 @@
mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState());
}
try {
- final int res = session.addToDisplay(window, window.mSeq, layoutParams,
+ final int res = session.addToDisplay(window, layoutParams,
View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrames.frame,
tmpFrames.contentInsets, tmpFrames.stableInsets, tmpFrames.displayCutout,
null /* outInputChannel */, mTmpInsetsState, mTempControls);
@@ -258,7 +258,7 @@
insetsState);
window.setOuter(snapshotSurface);
try {
- session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
+ session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
mTempControls, sTmpSurfaceSize, sTmpSurfaceControl);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d9c574c..d434bf9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -411,7 +411,7 @@
* @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
*/
static boolean sDisableCustomTaskAnimationProperty =
- SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, false);
+ SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
@@ -864,8 +864,7 @@
void updateSystemUiSettings() {
boolean changed;
synchronized (mGlobalLock) {
- changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext)
- || PolicyControl.reloadFromSetting(mContext);
+ changed = ImmersiveModeConfirmation.loadSetting(mCurrentUserId, mContext);
}
if (changed) {
updateRotation(false /* alwaysSendConfiguration */, false /* forceRelayout */);
@@ -1374,9 +1373,8 @@
return false;
}
- public int addWindow(Session session, IWindow client, int seq,
- LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
- Rect outContentInsets, Rect outStableInsets,
+ public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
+ int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
@@ -1557,7 +1555,7 @@
}
final WindowState win = new WindowState(this, session, client, token, parentWindow,
- appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
+ appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
if (win.mDeathRecipient == null) {
// Client has apparently died, so there is no reason to
@@ -2099,7 +2097,7 @@
== PackageManager.PERMISSION_GRANTED;
}
- public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
+ public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
@@ -2141,17 +2139,15 @@
if (attrs != null) {
displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
win.mToken.adjustWindowParams(win, attrs);
- // if they don't have the permission, mask out the status bar bits
- if (seq == win.mSeq) {
- int systemUiVisibility = attrs.systemUiVisibility
- | attrs.subtreeSystemUiVisibility;
- if ((systemUiVisibility & DISABLE_MASK) != 0) {
- if (!hasStatusBarPermission(pid, uid)) {
- systemUiVisibility &= ~DISABLE_MASK;
- }
+ int systemUiVisibility = attrs.systemUiVisibility
+ | attrs.subtreeSystemUiVisibility;
+ if ((systemUiVisibility & DISABLE_MASK) != 0) {
+ // if they don't have the permission, mask out the status bar bits
+ if (!hasStatusBarPermission(pid, uid)) {
+ systemUiVisibility &= ~DISABLE_MASK;
}
- win.mSystemUiVisibility = systemUiVisibility;
}
+ win.mSystemUiVisibility = systemUiVisibility;
if (win.mAttrs.type != attrs.type) {
throw new IllegalArgumentException(
"Window type can not be changed after the window is added.");
@@ -2427,8 +2423,8 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (winAnimator.mSurfaceController != null) {
- outSurfaceSize.set(winAnimator.mSurfaceController.getWidth(),
- winAnimator.mSurfaceController.getHeight());
+ win.calculateSurfaceBounds(win.getAttrs(), mTmpRect);
+ outSurfaceSize.set(mTmpRect.width(), mTmpRect.height());
}
getInsetsSourceControls(win, outActiveControls);
}
@@ -5731,31 +5727,13 @@
}
@Override
- public void statusBarVisibilityChanged(int displayId, int visibility) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Caller does not hold permission "
- + android.Manifest.permission.STATUS_BAR);
- }
-
- synchronized (mGlobalLock) {
- final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
- if (displayContent != null) {
- displayContent.statusBarVisibilityChanged(visibility);
- } else {
- Slog.w(TAG, "statusBarVisibilityChanged with invalid displayId=" + displayId);
- }
- }
- }
-
- @Override
public void hideTransientBars(int displayId) {
mAtmInternal.enforceCallerIsRecentsOrHasPermission(android.Manifest.permission.STATUS_BAR,
"hideTransientBars()");
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
- displayContent.hideTransientBars();
+ displayContent.getInsetsPolicy().hideTransient();
} else {
Slog.w(TAG, "hideTransientBars with invalid displayId=" + displayId);
}
@@ -6112,7 +6090,7 @@
}
if (inputMethodControlTarget != null) {
pw.print(" inputMethodControlTarget in display# "); pw.print(displayId);
- pw.print(' '); pw.println(inputMethodControlTarget.getWindow());
+ pw.print(' '); pw.println(inputMethodControlTarget);
}
});
pw.print(" mInTouchMode="); pw.println(mInTouchMode);
@@ -6164,7 +6142,6 @@
pw.print(" mRecentsAnimationController="); pw.println(mRecentsAnimationController);
mRecentsAnimationController.dump(pw, " ");
}
- PolicyControl.dump(" ", pw);
}
}
@@ -8270,4 +8247,14 @@
embeddedWindow.getName(), grantFocus);
}
}
+
+ @Override
+ public void holdLock(int durationMs) {
+ mContext.enforceCallingPermission(
+ Manifest.permission.INJECT_EVENTS, "holdLock requires shell identity");
+
+ synchronized (mGlobalLock) {
+ SystemClock.sleep(durationMs);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 268281b..4b8a398 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -221,6 +221,9 @@
@Nullable
private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+ /** The state for oom-adjustment calculation. */
+ private final OomScoreReferenceState mOomRefState;
+
public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
String name, int uid, int userId, Object owner, WindowProcessListener listener) {
mInfo = info;
@@ -232,6 +235,7 @@
mAtm = atm;
mDisplayId = INVALID_DISPLAY;
mBackgroundActivityStartCallback = mAtm.getBackgroundActivityStartCallback();
+ mOomRefState = new OomScoreReferenceState(this);
boolean isSysUiPackage = info.packageName.equals(
mAtm.getSysUiServiceComponentLocked().getPackageName());
@@ -688,15 +692,7 @@
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
public boolean hasVisibleActivities() {
- synchronized (mAtm.mGlobalLockWithoutBoost) {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
- if (r.mVisibleRequested) {
- return true;
- }
- }
- }
- return false;
+ return (mOomRefState.mActivityStateFlags & OomScoreReferenceState.FLAG_IS_VISIBLE) != 0;
}
@HotPath(caller = HotPath.LRU_UPDATE)
@@ -991,6 +987,34 @@
mHostActivities.remove(r);
}
+ private static class OomScoreReferenceState extends RootWindowContainer.LockedScheduler {
+ private static final int FLAG_IS_VISIBLE = 0x10000000;
+ private static final int FLAG_IS_PAUSING = 0x20000000;
+ private static final int FLAG_IS_STOPPING = 0x40000000;
+ private static final int FLAG_IS_STOPPING_FINISHING = 0x80000000;
+ /** @see Task#mLayerRank */
+ private static final int MASK_MIN_TASK_LAYER = 0x0000ffff;
+
+ private final WindowProcessController mOwner;
+ boolean mChanged;
+
+ /**
+ * The higher 16 bits are the activity states, and the lower 16 bits are the task layer
+ * rank. This field is written by window manager and read by activity manager.
+ */
+ volatile int mActivityStateFlags = MASK_MIN_TASK_LAYER;
+
+ OomScoreReferenceState(WindowProcessController owner) {
+ super(owner.mAtm);
+ mOwner = owner;
+ }
+
+ @Override
+ public void execute() {
+ mOwner.computeOomScoreReferenceStateIfNeeded();
+ }
+ }
+
public interface ComputeOomAdjCallback {
void onVisibleActivity();
void onPausedActivity();
@@ -998,64 +1022,102 @@
void onOtherActivity();
}
+ /**
+ * Returns the minimum task layer rank. It should only be called if {@link #hasActivities}
+ * returns {@code true}.
+ */
@HotPath(caller = HotPath.OOM_ADJUSTMENT)
- public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) {
+ public int computeOomAdjFromActivities(ComputeOomAdjCallback callback) {
+ final int flags = mOomRefState.mActivityStateFlags;
+ if ((flags & OomScoreReferenceState.FLAG_IS_VISIBLE) != 0) {
+ callback.onVisibleActivity();
+ } else if ((flags & OomScoreReferenceState.FLAG_IS_PAUSING) != 0) {
+ callback.onPausedActivity();
+ } else if ((flags & OomScoreReferenceState.FLAG_IS_STOPPING) != 0) {
+ callback.onStoppingActivity(
+ (flags & OomScoreReferenceState.FLAG_IS_STOPPING_FINISHING) != 0);
+ } else {
+ callback.onOtherActivity();
+ }
+ return flags & OomScoreReferenceState.MASK_MIN_TASK_LAYER;
+ }
+
+ void computeOomScoreReferenceStateIfNeeded() {
+ if (!mOomRefState.mChanged) {
+ return;
+ }
+ mOomRefState.mChanged = false;
+
// Since there could be more than one activities in a process record, we don't need to
// compute the OomAdj with each of them, just need to find out the activity with the
// "best" state, the order would be visible, pausing, stopping...
Task.ActivityState best = DESTROYED;
boolean finishing = true;
boolean visible = false;
- synchronized (mAtm.mGlobalLockWithoutBoost) {
- final int activitiesSize = mActivities.size();
- for (int j = 0; j < activitiesSize; j++) {
- final ActivityRecord r = mActivities.get(j);
- if (r.app != this) {
- Log.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
- + " instead of expected " + this);
- if (r.app == null || (r.app.mUid == mUid)) {
- // Only fix things up when they look sane
- r.setProcess(this);
- } else {
- continue;
- }
- }
- if (r.mVisibleRequested) {
- final Task task = r.getTask();
- if (task != null && minTaskLayer > 0) {
- final int layer = task.mLayerRank;
- if (layer >= 0 && minTaskLayer > layer) {
- minTaskLayer = layer;
- }
- }
- visible = true;
- // continue the loop, in case there are multiple visible activities in
- // this process, we'd find out the one with the minimal layer, thus it'll
- // get a higher adj score.
+ int minTaskLayer = Integer.MAX_VALUE;
+ for (int i = mActivities.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.app != this) {
+ Slog.e(TAG, "Found activity " + r + " in proc activity list using " + r.app
+ + " instead of expected " + this);
+ if (r.app == null || (r.app.mUid == mUid)) {
+ // Only fix things up when they look valid.
+ r.setProcess(this);
} else {
- if (best != PAUSING && best != PAUSED) {
- if (r.isState(PAUSING, PAUSED)) {
- best = PAUSING;
- } else if (r.isState(STOPPING)) {
- best = STOPPING;
- // Not "finishing" if any of activity isn't finishing.
- finishing &= r.finishing;
- }
- }
+ continue;
}
}
- }
- if (visible) {
- callback.onVisibleActivity();
- } else if (best == PAUSING) {
- callback.onPausedActivity();
- } else if (best == STOPPING) {
- callback.onStoppingActivity(finishing);
- } else {
- callback.onOtherActivity();
- }
+ if (r.mVisibleRequested) {
+ final Task task = r.getTask();
+ if (task != null && minTaskLayer > 0) {
+ final int layer = task.mLayerRank;
+ if (layer >= 0 && minTaskLayer > layer) {
+ minTaskLayer = layer;
+ }
+ }
+ visible = true;
+ // continue the loop, in case there are multiple visible activities in
+ // this process, we'd find out the one with the minimal layer, thus it'll
+ // get a higher adj score.
+ } else if (best != PAUSING && best != PAUSED) {
+ if (r.isState(PAUSING, PAUSED)) {
+ best = PAUSING;
+ } else if (r.isState(STOPPING)) {
+ best = STOPPING;
+ // Not "finishing" if any of activity isn't finishing.
+ finishing &= r.finishing;
+ }
+ }
- return minTaskLayer;
+ int stateFlags = minTaskLayer & OomScoreReferenceState.MASK_MIN_TASK_LAYER;
+ if (visible) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_VISIBLE;
+ } else if (best == PAUSING) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_PAUSING;
+ } else if (best == STOPPING) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_STOPPING;
+ if (finishing) {
+ stateFlags |= OomScoreReferenceState.FLAG_IS_STOPPING_FINISHING;
+ }
+ }
+ mOomRefState.mActivityStateFlags = stateFlags;
+ }
+ }
+
+ void invalidateOomScoreReferenceState(boolean computeNow) {
+ mOomRefState.mChanged = true;
+ if (computeNow) {
+ computeOomScoreReferenceStateIfNeeded();
+ return;
+ }
+ mOomRefState.scheduleIfNeeded();
+ }
+
+ /** Called when the process has some oom related changes and it is going to update oom-adj. */
+ private void prepareOomAdjustment() {
+ mAtm.mRootWindowContainer.rankTaskLayersIfNeeded();
+ // The task layer may not change but the activity state in the same task may change.
+ computeOomScoreReferenceStateIfNeeded();
}
public int computeRelaunchReason() {
@@ -1097,6 +1159,9 @@
if (addPendingTopUid) {
mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
}
+ if (updateOomAdj) {
+ prepareOomAdjustment();
+ }
// Posting on handler so WM lock isn't held when we call into AM.
final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
mListener, updateServiceConnectionActivities, activityChange, updateOomAdj);
@@ -1158,6 +1223,7 @@
if (topProcessState == ActivityManager.PROCESS_STATE_TOP) {
mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
}
+ prepareOomAdjustment();
// Posting the message at the front of queue so WM lock isn't held when we call into AM,
// and the process state of starting activity can be updated quicker which will give it a
// higher scheduling group.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1f7457c..ad28177 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -302,7 +302,6 @@
final boolean mIsImWindow;
final boolean mIsWallpaper;
private final boolean mIsFloatingLayer;
- int mSeq;
int mViewVisibility;
int mSystemUiVisibility;
@@ -834,10 +833,9 @@
}
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
- WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
- int viewVisibility, int ownerId, int showUserId,
- boolean ownerCanAddInternalSystemWindow) {
- this(service, s, c, token, parentWindow, appOp, seq, a, viewVisibility, ownerId, showUserId,
+ WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
+ int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
+ this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId,
ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
@Override
public void wakeUp(long time, @WakeReason int reason, String details) {
@@ -852,9 +850,9 @@
}
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
- WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
- int viewVisibility, int ownerId, int showUserId,
- boolean ownerCanAddInternalSystemWindow, PowerManagerWrapper powerManagerWrapper) {
+ WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
+ int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
+ PowerManagerWrapper powerManagerWrapper) {
super(service);
mSession = s;
mClient = c;
@@ -871,7 +869,6 @@
mPolicy = mWmService.mPolicy;
mContext = mWmService.mContext;
DeathRecipient deathRecipient = new DeathRecipient();
- mSeq = seq;
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
if (DEBUG) {
@@ -1412,15 +1409,14 @@
// Add a window that is using blastSync to the resizing list if it hasn't been reported
// already. This because the window is waiting on a finishDrawing from the client.
if (didFrameInsetsChange
- || winAnimator.mSurfaceResized
|| configChanged
|| dragResizingChanged
|| mReportOrientationChanged
|| shouldSendRedrawForSync()) {
ProtoLog.v(WM_DEBUG_RESIZE,
- "Resize reasons for w=%s: %s surfaceResized=%b configChanged=%b "
+ "Resize reasons for w=%s: %s configChanged=%b "
+ "dragResizingChanged=%b reportOrientationChanged=%b",
- this, mWindowFrames.getInsetsChangedInfo(), winAnimator.mSurfaceResized,
+ this, mWindowFrames.getInsetsChangedInfo(),
configChanged, dragResizingChanged, mReportOrientationChanged);
// If it's a dead window left on screen, and the configuration changed, there is nothing
@@ -3636,7 +3632,6 @@
// that may cause WINDOW_FREEZE_TIMEOUT because resizing the client keeps failing.
mReportOrientationChanged = false;
mDragResizingChangeReported = true;
- mWinAnimator.mSurfaceResized = false;
mWindowFrames.resetInsetsChanged();
final MergedConfiguration mergedConfiguration = mLastReportedConfiguration;
@@ -4031,7 +4026,7 @@
pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
+ " mHaveFrame=" + mHaveFrame
+ " mObscured=" + mObscured);
- pw.println(prefix + "mSeq=" + mSeq
+ pw.println(prefix
+ " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility));
}
if (!isVisibleByPolicy() || !mLegacyPolicyVisibilityAfterAnim || !mAppOpVisibility
@@ -4955,93 +4950,6 @@
}
/**
- * Calculate the window crop according to system decor policy. In general this is
- * the system decor rect (see #calculateSystemDecorRect), but we also have some
- * special cases. This rectangle is in screen space.
- */
- void calculatePolicyCrop(Rect policyCrop) {
- final DisplayContent displayContent = getDisplayContent();
-
- if (!displayContent.isDefaultDisplay && !displayContent.supportsSystemDecorations()) {
- // On a different display there is no system decor. Crop the window
- // by the screen boundaries.
- final DisplayInfo displayInfo = getDisplayInfo();
- policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
- mWindowFrames.mCompatFrame.height());
- policyCrop.intersect(-mWindowFrames.mCompatFrame.left, -mWindowFrames.mCompatFrame.top,
- displayInfo.logicalWidth - mWindowFrames.mCompatFrame.left,
- displayInfo.logicalHeight - mWindowFrames.mCompatFrame.top);
- } else if (skipDecorCrop()) {
- // Windows without policy decor aren't cropped.
- policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
- mWindowFrames.mCompatFrame.height());
- } else {
- // Crop to the system decor specified by policy.
- calculateSystemDecorRect(policyCrop);
- }
- }
-
- /**
- * The system decor rect is the region of the window which is not covered
- * by system decorations.
- */
- private void calculateSystemDecorRect(Rect systemDecorRect) {
- final Rect decorRect = mWindowFrames.mDecorFrame;
- final int width = mWindowFrames.mFrame.width();
- final int height = mWindowFrames.mFrame.height();
-
- final int left = mWindowFrames.mFrame.left;
- final int top = mWindowFrames.mFrame.top;
-
- // Initialize the decor rect to the entire frame.
- if (isDockedResizing()) {
- // If we are resizing with the divider, the task bounds might be smaller than the
- // stack bounds. The system decor is used to clip to the task bounds, which we don't
- // want in this case in order to avoid holes.
- //
- // We take care to not shrink the width, for surfaces which are larger than
- // the display region. Of course this area will not eventually be visible
- // but if we truncate the width now, we will calculate incorrectly
- // when adjusting to the stack bounds.
- final DisplayInfo displayInfo = getDisplayContent().getDisplayInfo();
- systemDecorRect.set(0, 0,
- Math.max(width, displayInfo.logicalWidth),
- Math.max(height, displayInfo.logicalHeight));
- } else {
- systemDecorRect.set(0, 0, width, height);
- }
-
- // If a freeform window is animating from a position where it would be cutoff, it would be
- // cutoff during the animation. We don't want that, so for the duration of the animation
- // we ignore the decor cropping and depend on layering to position windows correctly.
-
- // We also ignore cropping when the window is currently being drag resized in split screen
- // to prevent issues with the crop for screenshot.
- final boolean cropToDecor =
- !(inFreeformWindowingMode() && isAnimatingLw()) && !isDockedResizing();
- if (cropToDecor) {
- // Intersect with the decor rect, offsetted by window position.
- systemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
- decorRect.right - left, decorRect.bottom - top);
- }
-
- // If size compatibility is being applied to the window, the
- // surface is scaled relative to the screen. Also apply this
- // scaling to the crop rect. We aren't using the standard rect
- // scale function because we want to round things to make the crop
- // always round to a larger rect to ensure we don't crop too
- // much and hide part of the window that should be seen.
- if (mInvGlobalScale != 1.0f && inSizeCompatMode()) {
- final float scale = mInvGlobalScale;
- systemDecorRect.left = (int) (systemDecorRect.left * scale - 0.5f);
- systemDecorRect.top = (int) (systemDecorRect.top * scale - 0.5f);
- systemDecorRect.right = (int) ((systemDecorRect.right + 1) * scale - 0.5f);
- systemDecorRect.bottom = (int) ((systemDecorRect.bottom + 1) * scale - 0.5f);
- }
-
- }
-
- /**
* Expand the given rectangle by this windows surface insets. This
* takes you from the 'window size' to the 'surface size'.
* The surface insets are positive in each direction, so we inset by
@@ -5098,9 +5006,10 @@
// on the new one. This prevents blinking when we change elevation of freeform and
// pinned windows.
if (!mWinAnimator.tryChangeFormatInPlaceLocked()) {
- mWinAnimator.preserveSurfaceLocked();
+ mWinAnimator.preserveSurfaceLocked(getPendingTransaction());
result |= RELAYOUT_RES_SURFACE_CHANGED
| RELAYOUT_RES_FIRST_TIME;
+ scheduleAnimation();
}
}
@@ -5116,9 +5025,10 @@
// to preserve and destroy windows which are attached to another, they
// will keep their surface and its size may change over time.
if (mHasSurface && !isChildWindow()) {
- mWinAnimator.preserveSurfaceLocked();
+ mWinAnimator.preserveSurfaceLocked(getPendingTransaction());
result |= RELAYOUT_RES_SURFACE_CHANGED |
RELAYOUT_RES_FIRST_TIME;
+ scheduleAnimation();
}
}
final boolean freeformResizing = isDragResizing()
@@ -5942,4 +5852,37 @@
void requestRedrawForSync() {
mRedrawForSyncReported = false;
}
+
+ void calculateSurfaceBounds(WindowManager.LayoutParams attrs, Rect outSize) {
+ outSize.setEmpty();
+ if ((attrs.flags & FLAG_SCALED) != 0) {
+ // For a scaled surface, we always want the requested size.
+ outSize.right = mRequestedWidth;
+ outSize.bottom = mRequestedHeight;
+ } else {
+ // When we're doing a drag-resizing, request a surface that's fullscreen size,
+ // so that we don't need to reallocate during the process. This also prevents
+ // buffer drops due to size mismatch.
+ if (isDragResizing()) {
+ final DisplayInfo displayInfo = getDisplayInfo();
+ outSize.right = displayInfo.logicalWidth;
+ outSize.bottom = displayInfo.logicalHeight;
+ } else {
+ getCompatFrameSize(outSize);
+ }
+ }
+
+ // This doesn't necessarily mean that there is an error in the system. The sizes might be
+ // incorrect, because it is before the first layout or draw.
+ if (outSize.width() < 1) {
+ outSize.right = 1;
+ }
+ if (outSize.height() < 1) {
+ outSize.bottom = 1;
+ }
+
+ // Adjust for surface insets.
+ outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
+ -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 029c158..2ace23f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -121,12 +121,6 @@
boolean mAnimationIsEntrance;
- /**
- * Set when we have changed the size of the surface, to know that
- * we must tell them application to resize (and thus redraw itself).
- */
- boolean mSurfaceResized;
-
WindowSurfaceController mSurfaceController;
private WindowSurfaceController mPendingDestroySurface;
@@ -141,9 +135,6 @@
float mAlpha = 0;
float mLastAlpha = 0;
- Rect mTmpClipRect = new Rect();
- Rect mLastClipRect = new Rect();
- Rect mLastFinalClipRect = new Rect();
Rect mTmpStackBounds = new Rect();
private Rect mTmpAnimatingBounds = new Rect();
private Rect mTmpSourceBounds = new Rect();
@@ -358,7 +349,7 @@
return result;
}
- void preserveSurfaceLocked() {
+ void preserveSurfaceLocked(SurfaceControl.Transaction t) {
if (mDestroyPreservedSurfaceUponRedraw) {
// This could happen when switching the surface mode very fast. For example,
// we preserved a surface when dragResizing changed to true. Then before the
@@ -385,7 +376,7 @@
// Our SurfaceControl is always at layer 0 within the parent Surface managed by
// window-state. We want this old Surface to stay on top of the new one
// until we do the swap, so we place it at a positive layer.
- mSurfaceController.mSurfaceControl.setLayer(PRESERVED_SURFACE_LAYER);
+ t.setLayer(mSurfaceController.getClientViewRootSurface(), PRESERVED_SURFACE_LAYER);
}
mDestroyPreservedSurfaceUponRedraw = true;
mSurfaceDestroyDeferred = true;
@@ -462,7 +453,8 @@
flags |= SurfaceControl.SKIP_SCREENSHOT;
}
- calculateSurfaceBounds(w, attrs, mTmpSize);
+ w.calculateSurfaceBounds(attrs, mTmpSize);
+
final int width = mTmpSize.width();
final int height = mTmpSize.height();
@@ -474,9 +466,6 @@
+ " format=" + attrs.format + " flags=" + flags);
}
- // We may abort, so initialize to defaults.
- mLastClipRect.set(0, 0, 0, 0);
-
// Set up surface control with initial size.
try {
@@ -538,40 +527,6 @@
return mSurfaceController;
}
- private void calculateSurfaceBounds(WindowState w, LayoutParams attrs, Rect outSize) {
- outSize.setEmpty();
- if ((attrs.flags & FLAG_SCALED) != 0) {
- // For a scaled surface, we always want the requested size.
- outSize.right = w.mRequestedWidth;
- outSize.bottom = w.mRequestedHeight;
- } else {
- // When we're doing a drag-resizing, request a surface that's fullscreen size,
- // so that we don't need to reallocate during the process. This also prevents
- // buffer drops due to size mismatch.
- if (w.isDragResizing()) {
- final DisplayInfo displayInfo = w.getDisplayInfo();
- outSize.right = displayInfo.logicalWidth;
- outSize.bottom = displayInfo.logicalHeight;
- } else {
- w.getCompatFrameSize(outSize);
- }
- }
-
- // Something is wrong and SurfaceFlinger will not like this, try to revert to reasonable
- // values. This doesn't necessarily mean that there is an error in the system. The sizes
- // might be incorrect, because it is before the first layout or draw.
- if (outSize.width() < 1) {
- outSize.right = 1;
- }
- if (outSize.height() < 1) {
- outSize.bottom = 1;
- }
-
- // Adjust for surface insets.
- outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
- -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
- }
-
boolean hasSurface() {
return mSurfaceController != null && mSurfaceController.hasSurface();
}
@@ -680,77 +635,6 @@
mDsDy = mWin.mGlobalScale;
}
- /**
- * Calculate the window-space crop rect and fill clipRect.
- * @return true if clipRect has been filled otherwise, no window space crop should be applied.
- */
- private boolean calculateCrop(Rect clipRect) {
- final WindowState w = mWin;
- final DisplayContent displayContent = w.getDisplayContent();
- clipRect.setEmpty();
-
- if (displayContent == null) {
- return false;
- }
-
- if (w.getWindowConfiguration().tasksAreFloating()
- || WindowConfiguration.isSplitScreenWindowingMode(w.getWindowingMode())) {
- return false;
- }
-
- // During forced seamless rotation, the surface bounds get updated with the crop in the
- // new rotation, which is not compatible with showing the surface in the old rotation.
- // To work around that we disable cropping for such windows, as it is not necessary anyways.
- if (w.mForceSeamlesslyRotate) {
- return false;
- }
-
- // If we're animating, the wallpaper should only
- // be updated at the end of the animation.
- if (w.mAttrs.type == TYPE_WALLPAPER) {
- return false;
- }
-
- if (DEBUG_WINDOW_CROP) Slog.d(TAG,
- "Updating crop win=" + w + " mLastCrop=" + mLastClipRect);
-
- w.calculatePolicyCrop(mSystemDecorRect);
-
- if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
- + w.getDecorFrame() + " mSystemDecorRect=" + mSystemDecorRect);
-
- // We use the clip rect as provided by the tranformation for non-fullscreen windows to
- // avoid premature clipping with the system decor rect.
- clipRect.set(mSystemDecorRect);
- if (DEBUG_WINDOW_CROP) Slog.d(TAG, "win=" + w + " Initial clip rect: " + clipRect);
-
- w.expandForSurfaceInsets(clipRect);
-
- // The clip rect was generated assuming (0,0) as the window origin,
- // so we need to translate to match the actual surface coordinates.
- clipRect.offset(w.mAttrs.surfaceInsets.left, w.mAttrs.surfaceInsets.top);
-
- if (DEBUG_WINDOW_CROP) Slog.d(TAG,
- "win=" + w + " Clip rect after stack adjustment=" + clipRect);
-
- w.transformClipRectFromScreenToSurfaceSpace(clipRect);
-
- return true;
- }
-
- private void applyCrop(Rect clipRect, boolean recoveringMemory) {
- if (DEBUG_WINDOW_CROP) Slog.d(TAG, "applyCrop: win=" + mWin
- + " clipRect=" + clipRect);
- if (clipRect != null) {
- if (!clipRect.equals(mLastClipRect)) {
- mLastClipRect.set(clipRect);
- mSurfaceController.setCropInTransaction(clipRect, recoveringMemory);
- }
- } else {
- mSurfaceController.clearCropInTransaction(recoveringMemory);
- }
- }
-
private boolean shouldConsumeMainWindowSizeTransaction() {
// We only consume the transaction when the client is calling relayout
// because this is the only time we know the frameNumber will be valid
@@ -777,31 +661,6 @@
final LayoutParams attrs = mWin.getAttrs();
final Task task = w.getTask();
- calculateSurfaceBounds(w, attrs, mTmpSize);
-
- // Once relayout has been called at least once, we need to make sure
- // we only resize the client surface during calls to relayout. For
- // clients which use indeterminate measure specs (MATCH_PARENT),
- // we may try and change their window size without a call to relayout.
- // However, this would be unsafe, as the client may be in the middle
- // of producing a frame at the old size, having just completed layout
- // to find the surface size changed underneath it.
- final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
- if (relayout) {
- mSurfaceResized = mSurfaceController.setBufferSizeInTransaction(
- mTmpSize.width(), mTmpSize.height(), recoveringMemory);
- } else {
- mSurfaceResized = false;
- }
- // If we are undergoing seamless rotation, the surface has already
- // been set up to persist at it's old location. We need to freeze
- // updates until a resize occurs.
-
- Rect clipRect = null;
- if (calculateCrop(mTmpClipRect)) {
- clipRect = mTmpClipRect;
- }
-
if (shouldConsumeMainWindowSizeTransaction()) {
task.getMainWindowSizeChangeTask().getSurfaceControl().deferTransactionUntil(
mWin.getClientViewRootSurface(), mWin.getFrameNumber());
@@ -816,6 +675,11 @@
final Rect insets = attrs.surfaceInsets;
+ // getFrameNumber is only valid in the call-stack of relayoutWindow
+ // as this is the only-time we know the client renderer
+ // is paused.
+ final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
+
if (!w.mSeamlesslyRotated) {
// Used to offset the WSA when stack position changes before a resize.
int xOffset = mXOffset;
@@ -838,12 +702,6 @@
}
xOffset = -mTmpPos.x;
yOffset = -mTmpPos.y;
- // Crop also needs to be extended so the bottom isn't cut off when the WSA
- // position is moved.
- if (clipRect != null) {
- clipRect.right += mTmpPos.x;
- clipRect.bottom += mTmpPos.y;
- }
}
}
if (!mIsWallpaper) {
@@ -858,7 +716,6 @@
// Wallpaper is already updated above when calling setWallpaperPositionAndScale so
// we only need to consider the non-wallpaper case here.
if (!mIsWallpaper) {
- applyCrop(clipRect, recoveringMemory);
mSurfaceController.setMatrixInTransaction(
mDsDx * w.mHScale,
mDtDx * w.mVScale,
@@ -866,10 +723,6 @@
mDsDy * w.mVScale, recoveringMemory);
}
}
-
- if (mSurfaceResized) {
- mWin.getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
- }
}
/**
@@ -1064,7 +917,6 @@
mDtDy * mWin.mTmpMatrixArray[MSKEW_X] * mWin.mHScale,
mDsDy * mWin.mTmpMatrixArray[MSCALE_Y] * mWin.mVScale,
recoveringMemory);
- applyCrop(null, recoveringMemory);
}
/**
@@ -1255,7 +1107,6 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- mLastClipRect.dumpDebug(proto, LAST_CLIP_RECT);
if (mSurfaceController != null) {
mSurfaceController.dumpDebug(proto, SURFACE);
}
@@ -1276,11 +1127,7 @@
pw.print(prefix); pw.print(" mLastHidden="); pw.println(mLastHidden);
pw.print(prefix); pw.print("mEnterAnimationPending=" + mEnterAnimationPending);
pw.print(prefix); pw.print("mSystemDecorRect="); mSystemDecorRect.printShortString(pw);
- pw.print(" mLastClipRect="); mLastClipRect.printShortString(pw);
- if (!mLastFinalClipRect.isEmpty()) {
- pw.print(" mLastFinalClipRect="); mLastFinalClipRect.printShortString(pw);
- }
pw.println();
}
@@ -1288,8 +1135,7 @@
pw.print(prefix); pw.print("mPendingDestroySurface=");
pw.println(mPendingDestroySurface);
}
- if (mSurfaceResized || mSurfaceDestroyDeferred) {
- pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
+ if (mSurfaceDestroyDeferred) {
pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
}
if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 44462c3..30f6fa6 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -133,6 +133,7 @@
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",
"android.hardware.contexthub@1.0",
+ "android.hardware.gnss-cpp",
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 17a05f3..e39a3d1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -218,7 +218,7 @@
void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj);
void setFocusedDisplay(JNIEnv* env, int32_t displayId);
void setInputDispatchMode(bool enabled, bool frozen);
- void setSystemUiVisibility(int32_t visibility);
+ void setSystemUiLightsOut(bool lightsOut);
void setPointerSpeed(int32_t speed);
void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
void setShowTouches(bool enabled);
@@ -286,8 +286,8 @@
// Display size information.
std::vector<DisplayViewport> viewports;
- // System UI visibility.
- int32_t systemUiVisibility;
+ // True if System UI is less noticeable.
+ bool systemUiLightsOut;
// Pointer speed.
int32_t pointerSpeed;
@@ -339,7 +339,7 @@
{
AutoMutex _l(mLock);
- mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
+ mLocked.systemUiLightsOut = false;
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
@@ -366,8 +366,8 @@
}
{
AutoMutex _l(mLock);
- dump += StringPrintf(INDENT "System UI Visibility: 0x%0" PRIx32 "\n",
- mLocked.systemUiVisibility);
+ dump += StringPrintf(INDENT "System UI Lights Out: %s\n",
+ toString(mLocked.systemUiLightsOut));
dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
toString(mLocked.pointerGesturesEnabled));
@@ -811,11 +811,11 @@
mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
}
-void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
+void NativeInputManager::setSystemUiLightsOut(bool lightsOut) {
AutoMutex _l(mLock);
- if (mLocked.systemUiVisibility != visibility) {
- mLocked.systemUiVisibility = visibility;
+ if (mLocked.systemUiLightsOut != lightsOut) {
+ mLocked.systemUiLightsOut = lightsOut;
updateInactivityTimeoutLocked();
}
}
@@ -826,9 +826,8 @@
return;
}
- bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
- controller->setInactivityTimeout(lightsOut ? InactivityTimeout::SHORT
- : InactivityTimeout::NORMAL);
+ controller->setInactivityTimeout(mLocked.systemUiLightsOut ? InactivityTimeout::SHORT
+ : InactivityTimeout::NORMAL);
}
void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -1578,11 +1577,11 @@
im->setInputDispatchMode(enabled, frozen);
}
-static void nativeSetSystemUiVisibility(JNIEnv* /* env */,
- jclass /* clazz */, jlong ptr, jint visibility) {
+static void nativeSetSystemUiLightsOut(JNIEnv* /* env */, jclass /* clazz */, jlong ptr,
+ jboolean lightsOut) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setSystemUiVisibility(visibility);
+ im->setSystemUiLightsOut(lightsOut);
}
static jboolean nativeTransferTouchFocus(JNIEnv* env,
@@ -1802,7 +1801,7 @@
{"nativeSetFocusedDisplay", "(JI)V", (void*)nativeSetFocusedDisplay},
{"nativeSetPointerCapture", "(JZ)V", (void*)nativeSetPointerCapture},
{"nativeSetInputDispatchMode", "(JZZ)V", (void*)nativeSetInputDispatchMode},
- {"nativeSetSystemUiVisibility", "(JI)V", (void*)nativeSetSystemUiVisibility},
+ {"nativeSetSystemUiLightsOut", "(JZ)V", (void*)nativeSetSystemUiLightsOut},
{"nativeTransferTouchFocus", "(JLandroid/os/IBinder;Landroid/os/IBinder;)Z",
(void*)nativeTransferTouchFocus},
{"nativeSetPointerSpeed", "(JI)V", (void*)nativeSetPointerSpeed},
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4e53aa2..91645ba 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -30,9 +30,12 @@
#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
#include <android/hardware/gnss/2.1/IGnssMeasurement.h>
#include <android/hardware/gnss/3.0/IGnssPsds.h>
+#include <android/hardware/gnss/BnGnss.h>
+#include <android/hardware/gnss/BnGnssPsdsCallback.h>
#include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
#include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h>
#include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
+#include <binder/IServiceManager.h>
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
@@ -142,9 +145,10 @@
using android::OK;
using android::sp;
-using android::wp;
using android::status_t;
using android::String16;
+using android::wp;
+using android::binder::Status;
using android::hardware::Return;
using android::hardware::Void;
@@ -152,6 +156,7 @@
using android::hardware::hidl_string;
using android::hardware::hidl_death_recipient;
+using android::hardware::gnss::PsdsType;
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
@@ -161,11 +166,10 @@
using android::hardware::gnss::V1_0::IGnssNi;
using android::hardware::gnss::V1_0::IGnssNiCallback;
using android::hardware::gnss::V1_0::IGnssXtra;
+using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
using android::hardware::gnss::V3_0::IGnssPsds;
using android::hardware::gnss::V3_0::IGnssPsdsCallback;
-using android::hardware::gnss::V2_0::ElapsedRealtimeFlags;
-
using MeasurementCorrections_V1_0 = android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections;
using MeasurementCorrections_V1_1 = android::hardware::gnss::measurement_corrections::V1_1::MeasurementCorrections;
@@ -224,6 +228,10 @@
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl;
using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback;
+using IGnssAidl = android::hardware::gnss::IGnss;
+using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
+using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
+
struct GnssDeathRecipient : virtual public hidl_death_recipient
{
// hidl_death_recipient interface
@@ -245,7 +253,9 @@
sp<IGnss_V2_0> gnssHal_V2_0 = nullptr;
sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
sp<IGnss_V3_0> gnssHal_V3_0 = nullptr;
+sp<IGnssAidl> gnssHalAidl = nullptr;
sp<IGnssPsds> gnssPsdsIface = nullptr;
+sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
@@ -445,6 +455,19 @@
}
}
+static jboolean checkAidlStatus(const Status& status, const char* errorMessage,
+ const bool success) {
+ if (!status.isOk()) {
+ ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str());
+ return JNI_FALSE;
+ }
+ if (!success) {
+ ALOGE("AIDL return failure: %s", errorMessage);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion,
method_halInterfaceVersionCtor, major, minor);
@@ -928,20 +951,31 @@
* GnssPsdsCallback class implements the callback methods for the IGnssPsds
* interface.
*/
+struct GnssPsdsCallbackAidl : public android::hardware::gnss::BnGnssPsdsCallback {
+ Status downloadRequestCb(PsdsType psdsType) override {
+ ALOGD("%s. psdsType: %d", __func__, static_cast<int32_t>(psdsType));
+ JNIEnv* env = getJniEnv();
+ env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return Status::ok();
+ }
+};
+
+/*
+ * GnssPsdsCallback class implements the callback methods for the IGnssPsds
+ * interface.
+ */
struct GnssPsdsCallback : public IGnssPsdsCallback {
Return<void> downloadRequestCb() override;
Return<void> downloadRequestCb_3_0(int32_t psdsType) override;
};
Return<void> GnssPsdsCallback::downloadRequestCb() {
- JNIEnv* env = getJniEnv();
- env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, /* psdsType= */ 1);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return Void();
+ return downloadRequestCb_3_0(/* psdsType= */ 1);
}
Return<void> GnssPsdsCallback::downloadRequestCb_3_0(int32_t psdsType) {
- ALOGD("%s: %d", __func__, psdsType);
+ ALOGD("%s. psdsType: %d", __func__, psdsType);
JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_psdsDownloadRequest, psdsType);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -1917,6 +1951,11 @@
/* Initializes the GNSS service handle. */
static void android_location_GnssLocationProvider_set_gps_service_handle() {
+ gnssHalAidl = waitForVintfService<IGnssAidl>();
+ if (gnssHalAidl != nullptr) {
+ ALOGD("Successfully got GNSS AIDL handle.");
+ }
+
ALOGD("Trying IGnss_V3_0::getService()");
gnssHal_V3_0 = IGnss_V3_0::getService();
if (gnssHal_V3_0 != nullptr) {
@@ -2168,7 +2207,15 @@
ALOGD("Link to death notification successful");
}
- if (gnssHal_V3_0 != nullptr) {
+ if (gnssHalAidl != nullptr) {
+ sp<IGnssPsdsAidl> gnssPsdsAidl;
+ auto status = gnssHalAidl->getExtensionPsds(&gnssPsdsAidl);
+ if (status.isOk()) {
+ gnssPsdsAidlIface = gnssPsdsAidl;
+ } else {
+ ALOGD("Unable to get a handle to PSDS AIDL interface.");
+ }
+ } else if (gnssHal_V3_0 != nullptr) {
auto gnssPsds = gnssHal_V3_0->getExtensionPsds();
if (!gnssPsds.isOk()) {
ALOGD("Unable to get a handle to Psds");
@@ -2470,19 +2517,28 @@
}
// Set IGnssPsds or IGnssXtra callback.
- sp<IGnssPsdsCallback> gnssPsdsCbIface = new GnssPsdsCallback();
- if (gnssPsdsIface != nullptr) {
- result = gnssPsdsIface->setCallback_3_0(gnssPsdsCbIface);
- if (!checkHidlReturn(result, "IGnssPsds setCallback() failed.")) {
- gnssPsdsIface = nullptr;
- }
- } else if (gnssXtraIface != nullptr) {
- result = gnssXtraIface->setCallback(gnssPsdsCbIface);
- if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
- gnssXtraIface = nullptr;
+ if (gnssPsdsAidlIface != nullptr) {
+ sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
+ bool success;
+ auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl, &success);
+ if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.", success)) {
+ gnssPsdsAidlIface = nullptr;
}
} else {
- ALOGI("Unable to initialize IGnssPsds/IGnssXtra interface.");
+ sp<IGnssPsdsCallback> gnssPsdsCbIface = new GnssPsdsCallback();
+ if (gnssPsdsIface != nullptr) {
+ result = gnssPsdsIface->setCallback_3_0(gnssPsdsCbIface);
+ if (!checkHidlReturn(result, "IGnssPsds setCallback() failed.")) {
+ gnssPsdsIface = nullptr;
+ }
+ } else if (gnssXtraIface != nullptr) {
+ result = gnssXtraIface->setCallback(gnssPsdsCbIface);
+ if (!checkHidlReturn(result, "IGnssXtra setCallback() failed.")) {
+ gnssXtraIface = nullptr;
+ }
+ } else {
+ ALOGI("Unable to initialize IGnssPsds/IGnssXtra interface.");
+ }
}
// Set IAGnss.hal callback.
@@ -2743,19 +2799,29 @@
static jboolean android_location_GnssLocationProvider_supports_psds(
JNIEnv* /* env */, jobject /* obj */) {
- return (gnssPsdsIface != nullptr || gnssXtraIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+ return (gnssPsdsAidlIface != nullptr || gnssPsdsIface != nullptr || gnssXtraIface != nullptr)
+ ? JNI_TRUE
+ : JNI_FALSE;
}
static void android_location_GnssLocationProvider_inject_psds_data(JNIEnv* env, jobject /* obj */,
jbyteArray data, jint length,
jint psdsType) {
- if (gnssPsdsIface == nullptr && gnssXtraIface == nullptr) {
+ if (gnssPsdsAidlIface == nullptr && gnssPsdsIface == nullptr && gnssXtraIface == nullptr) {
ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__);
return;
}
jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0));
- if (gnssPsdsIface != nullptr) {
+ if (gnssPsdsAidlIface != nullptr) {
+ bool success;
+ auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType),
+ std::vector<uint8_t>((const uint8_t*)bytes,
+ (const uint8_t*)bytes +
+ length),
+ &success);
+ checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.", success);
+ } else if (gnssPsdsIface != nullptr) {
auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType,
std::string((const char*)bytes, length));
checkHidlReturn(result, "IGnssPsds injectPsdsData() failed.");
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index e8bf468..bbcb312 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1468,7 +1468,7 @@
// Need a shared pointer: will be passing it into all unpacking jobs.
std::shared_ptr<ZipArchive> zipFile(zipFileHandle, [](ZipArchiveHandle h) { CloseArchive(h); });
void* cookie = nullptr;
- const auto libFilePrefix = path::join(constants().libDir, abi);
+ const auto libFilePrefix = path::join(constants().libDir, abi) + "/";
if (StartIteration(zipFile.get(), &cookie, libFilePrefix, constants().libSuffix)) {
LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
return false;
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 33317a3..8b1e9c5 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -40,7 +40,6 @@
import com.android.server.SystemService;
import com.android.server.people.data.DataManager;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -95,28 +94,57 @@
* @throws SecurityException if the caller is not system or root
*/
private static void enforceSystemOrRoot(String message) {
- int uid = Binder.getCallingUid();
- if (!UserHandle.isSameApp(uid, Process.SYSTEM_UID) && uid != Process.ROOT_UID) {
+ if (!isSystemOrRoot()) {
throw new SecurityException("Only system may " + message);
}
}
+ private static boolean isSystemOrRoot() {
+ final int uid = Binder.getCallingUid();
+ return UserHandle.isSameApp(uid, Process.SYSTEM_UID) || uid == Process.ROOT_UID;
+ }
+
+
+ /**
+ * Enforces that only the system, root UID or SystemUI can make certain calls.
+ *
+ * @param message used as message if SecurityException is thrown
+ * @throws SecurityException if the caller is not system or root
+ */
+ private static void enforceSystemRootOrSystemUI(Context context, String message) {
+ if (isSystemOrRoot()) return;
+ context.enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ message);
+ }
+
private final class BinderService extends IPeopleManager.Stub {
@Override
public ParceledListSlice<ConversationChannel> getRecentConversations() {
enforceSystemOrRoot("get recent conversations");
- return new ParceledListSlice<>(new ArrayList<>());
+ return new ParceledListSlice<>(
+ mDataManager.getRecentConversations(
+ Binder.getCallingUserHandle().getIdentifier()));
}
@Override
public void removeRecentConversation(String packageName, int userId, String shortcutId) {
enforceSystemOrRoot("remove a recent conversation");
+ mDataManager.removeRecentConversation(packageName, userId, shortcutId,
+ Binder.getCallingUserHandle().getIdentifier());
}
@Override
public void removeAllRecentConversations() {
enforceSystemOrRoot("remove all recent conversations");
+ mDataManager.removeAllRecentConversations(
+ Binder.getCallingUserHandle().getIdentifier());
+ }
+
+ @Override
+ public long getLastInteraction(String packageName, int userId, String shortcutId) {
+ enforceSystemRootOrSystemUI(getContext(), "get last interaction");
+ return mDataManager.getLastInteraction(packageName, userId, shortcutId);
}
}
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index 1737828..45f389c 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -90,6 +90,11 @@
@Nullable
private String mNotificationChannelId;
+ @Nullable
+ private String mParentNotificationChannelId;
+
+ private long mLastEventTimestamp;
+
@ShortcutFlags
private int mShortcutFlags;
@@ -102,6 +107,8 @@
mContactUri = builder.mContactUri;
mContactPhoneNumber = builder.mContactPhoneNumber;
mNotificationChannelId = builder.mNotificationChannelId;
+ mParentNotificationChannelId = builder.mParentNotificationChannelId;
+ mLastEventTimestamp = builder.mLastEventTimestamp;
mShortcutFlags = builder.mShortcutFlags;
mConversationFlags = builder.mConversationFlags;
}
@@ -129,14 +136,32 @@
}
/**
- * ID of the {@link android.app.NotificationChannel} where the notifications for this
- * conversation are posted.
+ * ID of the conversation-specific {@link android.app.NotificationChannel} where the
+ * notifications for this conversation are posted.
*/
@Nullable
String getNotificationChannelId() {
return mNotificationChannelId;
}
+ /**
+ * ID of the parent {@link android.app.NotificationChannel} for this conversation. This is the
+ * notification channel where the notifications are posted before this conversation is
+ * customized by the user.
+ */
+ @Nullable
+ String getParentNotificationChannelId() {
+ return mParentNotificationChannelId;
+ }
+
+ /**
+ * Timestamp of the last event, {@code 0L} if there are no events. This timestamp is for
+ * identifying and sorting the recent conversations. It may only count a subset of event types.
+ */
+ long getLastEventTimestamp() {
+ return mLastEventTimestamp;
+ }
+
/** Whether the shortcut for this conversation is set long-lived by the app. */
public boolean isShortcutLongLived() {
return hasShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED);
@@ -202,6 +227,8 @@
&& Objects.equals(mContactUri, other.mContactUri)
&& Objects.equals(mContactPhoneNumber, other.mContactPhoneNumber)
&& Objects.equals(mNotificationChannelId, other.mNotificationChannelId)
+ && Objects.equals(mParentNotificationChannelId, other.mParentNotificationChannelId)
+ && Objects.equals(mLastEventTimestamp, other.mLastEventTimestamp)
&& mShortcutFlags == other.mShortcutFlags
&& mConversationFlags == other.mConversationFlags;
}
@@ -209,7 +236,8 @@
@Override
public int hashCode() {
return Objects.hash(mShortcutId, mLocusId, mContactUri, mContactPhoneNumber,
- mNotificationChannelId, mShortcutFlags, mConversationFlags);
+ mNotificationChannelId, mParentNotificationChannelId, mLastEventTimestamp,
+ mShortcutFlags, mConversationFlags);
}
@Override
@@ -221,6 +249,8 @@
sb.append(", contactUri=").append(mContactUri);
sb.append(", phoneNumber=").append(mContactPhoneNumber);
sb.append(", notificationChannelId=").append(mNotificationChannelId);
+ sb.append(", parentNotificationChannelId=").append(mParentNotificationChannelId);
+ sb.append(", lastEventTimestamp=").append(mLastEventTimestamp);
sb.append(", shortcutFlags=0x").append(Integer.toHexString(mShortcutFlags));
sb.append(" [");
if (isShortcutLongLived()) {
@@ -280,6 +310,11 @@
protoOutputStream.write(ConversationInfoProto.NOTIFICATION_CHANNEL_ID,
mNotificationChannelId);
}
+ if (mParentNotificationChannelId != null) {
+ protoOutputStream.write(ConversationInfoProto.PARENT_NOTIFICATION_CHANNEL_ID,
+ mParentNotificationChannelId);
+ }
+ protoOutputStream.write(ConversationInfoProto.LAST_EVENT_TIMESTAMP, mLastEventTimestamp);
protoOutputStream.write(ConversationInfoProto.SHORTCUT_FLAGS, mShortcutFlags);
protoOutputStream.write(ConversationInfoProto.CONVERSATION_FLAGS, mConversationFlags);
if (mContactPhoneNumber != null) {
@@ -300,6 +335,8 @@
out.writeInt(mShortcutFlags);
out.writeInt(mConversationFlags);
out.writeUTF(mContactPhoneNumber != null ? mContactPhoneNumber : "");
+ out.writeUTF(mParentNotificationChannelId != null ? mParentNotificationChannelId : "");
+ out.writeLong(mLastEventTimestamp);
} catch (IOException e) {
Slog.e(TAG, "Failed to write fields to backup payload.", e);
return null;
@@ -338,6 +375,14 @@
builder.setNotificationChannelId(protoInputStream.readString(
ConversationInfoProto.NOTIFICATION_CHANNEL_ID));
break;
+ case (int) ConversationInfoProto.PARENT_NOTIFICATION_CHANNEL_ID:
+ builder.setParentNotificationChannelId(protoInputStream.readString(
+ ConversationInfoProto.PARENT_NOTIFICATION_CHANNEL_ID));
+ break;
+ case (int) ConversationInfoProto.LAST_EVENT_TIMESTAMP:
+ builder.setLastEventTimestamp(protoInputStream.readLong(
+ ConversationInfoProto.LAST_EVENT_TIMESTAMP));
+ break;
case (int) ConversationInfoProto.SHORTCUT_FLAGS:
builder.setShortcutFlags(protoInputStream.readInt(
ConversationInfoProto.SHORTCUT_FLAGS));
@@ -382,6 +427,11 @@
if (!TextUtils.isEmpty(contactPhoneNumber)) {
builder.setContactPhoneNumber(contactPhoneNumber);
}
+ String parentNotificationChannelId = in.readUTF();
+ if (!TextUtils.isEmpty(parentNotificationChannelId)) {
+ builder.setParentNotificationChannelId(parentNotificationChannelId);
+ }
+ builder.setLastEventTimestamp(in.readLong());
} catch (IOException e) {
Slog.e(TAG, "Failed to read conversation info fields from backup payload.", e);
return null;
@@ -408,6 +458,11 @@
@Nullable
private String mNotificationChannelId;
+ @Nullable
+ private String mParentNotificationChannelId;
+
+ private long mLastEventTimestamp;
+
@ShortcutFlags
private int mShortcutFlags;
@@ -427,6 +482,8 @@
mContactUri = conversationInfo.mContactUri;
mContactPhoneNumber = conversationInfo.mContactPhoneNumber;
mNotificationChannelId = conversationInfo.mNotificationChannelId;
+ mParentNotificationChannelId = conversationInfo.mParentNotificationChannelId;
+ mLastEventTimestamp = conversationInfo.mLastEventTimestamp;
mShortcutFlags = conversationInfo.mShortcutFlags;
mConversationFlags = conversationInfo.mConversationFlags;
}
@@ -456,6 +513,16 @@
return this;
}
+ Builder setParentNotificationChannelId(String parentNotificationChannelId) {
+ mParentNotificationChannelId = parentNotificationChannelId;
+ return this;
+ }
+
+ Builder setLastEventTimestamp(long lastEventTimestamp) {
+ mLastEventTimestamp = lastEventTimestamp;
+ return this;
+ }
+
Builder setShortcutFlags(@ShortcutFlags int shortcutFlags) {
mShortcutFlags = shortcutFlags;
return this;
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 52fec33..87f2c58 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -24,6 +24,7 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
+import android.app.people.ConversationChannel;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.usage.UsageEvents;
@@ -74,9 +75,11 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.PriorityQueue;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -97,6 +100,7 @@
private static final long QUERY_EVENTS_MAX_AGE_MS = 5L * DateUtils.MINUTE_IN_MILLIS;
private static final long USAGE_STATS_QUERY_INTERVAL_SEC = 120L;
+ @VisibleForTesting static final int MAX_CACHED_RECENT_SHORTCUTS = 30;
private final Context mContext;
private final Injector mInjector;
@@ -209,6 +213,83 @@
mContext.getPackageName(), intentFilter, callingUserId);
}
+ /** Returns the cached non-customized recent conversations. */
+ public List<ConversationChannel> getRecentConversations(@UserIdInt int callingUserId) {
+ List<ConversationChannel> conversationChannels = new ArrayList<>();
+ forPackagesInProfile(callingUserId, packageData -> {
+ String packageName = packageData.getPackageName();
+ int userId = packageData.getUserId();
+ packageData.forAllConversations(conversationInfo -> {
+ if (!isCachedRecentConversation(conversationInfo)) {
+ return;
+ }
+ String shortcutId = conversationInfo.getShortcutId();
+ ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
+ int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
+ NotificationChannel parentChannel =
+ mNotificationManagerInternal.getNotificationChannel(packageName, uid,
+ conversationInfo.getParentNotificationChannelId());
+ if (shortcutInfo == null || parentChannel == null) {
+ return;
+ }
+ conversationChannels.add(
+ new ConversationChannel(shortcutInfo, parentChannel,
+ conversationInfo.getLastEventTimestamp(),
+ hasActiveNotifications(packageName, userId, shortcutId)));
+ });
+ });
+ return conversationChannels;
+ }
+
+ /**
+ * Uncaches the shortcut that's associated with the specified conversation so this conversation
+ * will not show up in the recent conversations list.
+ */
+ public void removeRecentConversation(String packageName, int userId, String shortcutId,
+ @UserIdInt int callingUserId) {
+ if (!hasActiveNotifications(packageName, userId, shortcutId)) {
+ mShortcutServiceInternal.uncacheShortcuts(callingUserId, mContext.getPackageName(),
+ packageName, Collections.singletonList(shortcutId), userId,
+ ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ }
+ }
+
+ /**
+ * Uncaches the shortcuts for all the recent conversations that they don't have active
+ * notifications.
+ */
+ public void removeAllRecentConversations(@UserIdInt int callingUserId) {
+ forPackagesInProfile(callingUserId, packageData -> {
+ String packageName = packageData.getPackageName();
+ int userId = packageData.getUserId();
+ List<String> idsToUncache = new ArrayList<>();
+ packageData.forAllConversations(conversationInfo -> {
+ String shortcutId = conversationInfo.getShortcutId();
+ if (isCachedRecentConversation(conversationInfo)
+ && !hasActiveNotifications(packageName, userId, shortcutId)) {
+ idsToUncache.add(shortcutId);
+ }
+ });
+ mShortcutServiceInternal.uncacheShortcuts(callingUserId, mContext.getPackageName(),
+ packageName, idsToUncache, userId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ });
+ }
+
+ /**
+ * Returns the last notification interaction with the specified conversation. If the
+ * conversation can't be found or no interactions have been recorded, returns 0L.
+ */
+ public long getLastInteraction(String packageName, int userId, String shortcutId) {
+ final PackageData packageData = getPackage(packageName, userId);
+ if (packageData != null) {
+ final ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId);
+ if (conversationInfo != null) {
+ return conversationInfo.getLastEventTimestamp();
+ }
+ }
+ return 0L;
+ }
+
/** Reports the sharing related {@link AppTargetEvent} from App Prediction Manager. */
public void reportShareTargetEvent(@NonNull AppTargetEvent event,
@NonNull IntentFilter intentFilter) {
@@ -278,7 +359,6 @@
}
pruneUninstalledPackageData(userData);
- final NotificationListener notificationListener = mNotificationListeners.get(userId);
userData.forAllPackages(packageData -> {
if (signal.isCanceled()) {
return;
@@ -291,20 +371,7 @@
packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS);
}
packageData.pruneOrphanEvents();
- if (notificationListener != null) {
- String packageName = packageData.getPackageName();
- packageData.forAllConversations(conversationInfo -> {
- if (conversationInfo.isShortcutCachedForNotification()
- && conversationInfo.getNotificationChannelId() == null
- && !notificationListener.hasActiveNotifications(
- packageName, conversationInfo.getShortcutId())) {
- mShortcutServiceInternal.uncacheShortcuts(userId,
- mContext.getPackageName(), packageName,
- Collections.singletonList(conversationInfo.getShortcutId()),
- userId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- }
- });
- }
+ cleanupCachedShortcuts(userId, MAX_CACHED_RECENT_SHORTCUTS);
});
}
@@ -467,7 +534,8 @@
@NonNull String packageName, @UserIdInt int userId,
@Nullable List<String> shortcutIds) {
@ShortcutQuery.QueryFlags int queryFlags = ShortcutQuery.FLAG_MATCH_DYNAMIC
- | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
+ | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
+ | ShortcutQuery.FLAG_MATCH_CACHED;
return mShortcutServiceInternal.getShortcuts(
UserHandle.USER_SYSTEM, mContext.getPackageName(),
/*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null,
@@ -527,6 +595,68 @@
return packageData;
}
+ private boolean isCachedRecentConversation(ConversationInfo conversationInfo) {
+ return conversationInfo.isShortcutCachedForNotification()
+ && conversationInfo.getNotificationChannelId() == null
+ && conversationInfo.getParentNotificationChannelId() != null
+ && conversationInfo.getLastEventTimestamp() > 0L;
+ }
+
+ private boolean hasActiveNotifications(String packageName, @UserIdInt int userId,
+ String shortcutId) {
+ NotificationListener notificationListener = mNotificationListeners.get(userId);
+ return notificationListener != null
+ && notificationListener.hasActiveNotifications(packageName, shortcutId);
+ }
+
+ /**
+ * Cleans up the oldest cached shortcuts that don't have active notifications for the recent
+ * conversations. After the cleanup, normally, the total number of cached shortcuts will be
+ * less than or equal to the target count. However, there are exception cases: e.g. when all
+ * the existing cached shortcuts have active notifications.
+ */
+ private void cleanupCachedShortcuts(@UserIdInt int userId, int targetCachedCount) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData == null) {
+ return;
+ }
+ // pair of <package name, conversation info>
+ List<Pair<String, ConversationInfo>> cachedConvos = new ArrayList<>();
+ userData.forAllPackages(packageData ->
+ packageData.forAllConversations(conversationInfo -> {
+ if (isCachedRecentConversation(conversationInfo)) {
+ cachedConvos.add(
+ Pair.create(packageData.getPackageName(), conversationInfo));
+ }
+ })
+ );
+ if (cachedConvos.size() <= targetCachedCount) {
+ return;
+ }
+ int numToUncache = cachedConvos.size() - targetCachedCount;
+ // Max heap keeps the oldest cached conversations.
+ PriorityQueue<Pair<String, ConversationInfo>> maxHeap = new PriorityQueue<>(
+ numToUncache + 1,
+ Comparator.comparingLong((Pair<String, ConversationInfo> pair) ->
+ pair.second.getLastEventTimestamp()).reversed());
+ for (Pair<String, ConversationInfo> cached : cachedConvos) {
+ if (hasActiveNotifications(cached.first, userId, cached.second.getShortcutId())) {
+ continue;
+ }
+ maxHeap.offer(cached);
+ if (maxHeap.size() > numToUncache) {
+ maxHeap.poll();
+ }
+ }
+ while (!maxHeap.isEmpty()) {
+ Pair<String, ConversationInfo> toUncache = maxHeap.poll();
+ mShortcutServiceInternal.uncacheShortcuts(userId,
+ mContext.getPackageName(), toUncache.first,
+ Collections.singletonList(toUncache.second.getShortcutId()),
+ userId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ }
+ }
+
@VisibleForTesting
@WorkerThread
void addOrUpdateConversationInfo(@NonNull ShortcutInfo shortcutInfo) {
@@ -737,9 +867,21 @@
public void onShortcutsAddedOrUpdated(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {
mInjector.getBackgroundExecutor().execute(() -> {
+ PackageData packageData = getPackage(packageName, user.getIdentifier());
for (ShortcutInfo shortcut : shortcuts) {
if (ShortcutHelper.isConversationShortcut(
shortcut, mShortcutServiceInternal, user.getIdentifier())) {
+ if (shortcut.isCached()) {
+ ConversationInfo conversationInfo = packageData != null
+ ? packageData.getConversationInfo(shortcut.getId()) : null;
+ if (conversationInfo == null
+ || !conversationInfo.isShortcutCachedForNotification()) {
+ // This is a newly cached shortcut. Clean up the existing cached
+ // shortcuts to ensure the cache size is under the limit.
+ cleanupCachedShortcuts(user.getIdentifier(),
+ MAX_CACHED_RECENT_SHORTCUTS - 1);
+ }
+ }
addOrUpdateConversationInfo(shortcut);
}
}
@@ -800,6 +942,16 @@
});
if (packageData != null) {
+ ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId);
+ if (conversationInfo == null) {
+ return;
+ }
+ ConversationInfo updated = new ConversationInfo.Builder(conversationInfo)
+ .setLastEventTimestamp(sbn.getPostTime())
+ .setParentNotificationChannelId(sbn.getNotification().getChannelId())
+ .build();
+ packageData.getConversationStore().addOrUpdate(updated);
+
EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
eventHistory.addEvent(new Event(sbn.getPostTime(), Event.TYPE_NOTIFICATION_POSTED));
@@ -820,16 +972,7 @@
int count = mActiveNotifCounts.getOrDefault(conversationKey, 0) - 1;
if (count <= 0) {
mActiveNotifCounts.remove(conversationKey);
- // The shortcut was cached by Notification Manager synchronously when the
- // associated notification was posted. Uncache it here when all the
- // associated notifications are removed.
- if (conversationInfo.isShortcutCachedForNotification()
- && conversationInfo.getNotificationChannelId() == null) {
- mShortcutServiceInternal.uncacheShortcuts(mUserId,
- mContext.getPackageName(), sbn.getPackageName(),
- Collections.singletonList(conversationInfo.getShortcutId()),
- mUserId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- }
+ cleanupCachedShortcuts(mUserId, MAX_CACHED_RECENT_SHORTCUTS);
} else {
mActiveNotifCounts.put(conversationKey, count);
}
@@ -885,24 +1028,6 @@
conversationStore.addOrUpdate(builder.build());
}
- synchronized void cleanupCachedShortcuts() {
- for (Pair<String, String> conversationKey : mActiveNotifCounts.keySet()) {
- String packageName = conversationKey.first;
- String shortcutId = conversationKey.second;
- PackageData packageData = getPackage(packageName, mUserId);
- ConversationInfo conversationInfo =
- packageData != null ? packageData.getConversationInfo(shortcutId) : null;
- if (conversationInfo != null
- && conversationInfo.isShortcutCachedForNotification()
- && conversationInfo.getNotificationChannelId() == null) {
- mShortcutServiceInternal.uncacheShortcuts(mUserId,
- mContext.getPackageName(), packageName,
- Collections.singletonList(shortcutId),
- mUserId, ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- }
- }
- }
-
synchronized boolean hasActiveNotifications(String packageName, String shortcutId) {
return mActiveNotifCounts.containsKey(Pair.create(packageName, shortcutId));
}
@@ -975,16 +1100,7 @@
@Override
public void onReceive(Context context, Intent intent) {
- forAllUnlockedUsers(userData -> {
- NotificationListener listener = mNotificationListeners.get(userData.getUserId());
- // Clean up the cached shortcuts because all the notifications are cleared after
- // system shutdown. The associated shortcuts need to be uncached to keep in sync
- // unless the settings are changed by the user.
- if (listener != null) {
- listener.cleanupCachedShortcuts();
- }
- userData.forAllPackages(PackageData::saveToDisk);
- });
+ forAllUnlockedUsers(userData -> userData.forAllPackages(PackageData::saveToDisk));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 5d8f662..a250c21 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -351,7 +351,7 @@
doReturn(mock(WindowProcessController.class)).when(app).getWindowProcessController();
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasActivities();
- doAnswer(answer((minTaskLayer, callback) -> {
+ doAnswer(answer(callback -> {
Field field = callback.getClass().getDeclaredField("adj");
field.set(callback, VISIBLE_APP_ADJ);
field = callback.getClass().getDeclaredField("foregroundActivities");
@@ -361,7 +361,7 @@
field = callback.getClass().getDeclaredField("schedGroup");
field.set(callback, SCHED_GROUP_TOP_APP);
return 0;
- })).when(wpc).computeOomAdjFromActivities(anyInt(),
+ })).when(wpc).computeOomAdjFromActivities(
any(WindowProcessController.ComputeOomAdjCallback.class));
sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
sService.mOomAdjuster.updateOomAdjLocked(app, false, OomAdjuster.OOM_ADJ_REASON_NONE);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 90e1cfc..79936ce 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -77,6 +77,7 @@
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.DUMP"/>
<uses-permission android:name="android.permission.READ_DREAM_STATE"/>
+ <uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION"/>
<uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index b7355ce..b9c2e56 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -49,6 +49,7 @@
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
+import com.android.server.accessibility.magnification.MagnificationController;
import com.android.server.accessibility.magnification.WindowMagnificationManager;
import com.android.server.accessibility.test.MessageCapturingHandler;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -94,6 +95,7 @@
@Mock private IBinder mMockBinder;
@Mock private IAccessibilityServiceClient mMockServiceClient;
@Mock private WindowMagnificationManager mMockWindowMagnificationMgr;
+ @Mock private MagnificationController mMockMagnificationController;
private AccessibilityUserState mUserState;
private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
@@ -110,6 +112,8 @@
LocalServices.addService(
ActivityTaskManagerInternal.class, mMockActivityTaskManagerInternal);
+ when(mMockMagnificationController.getWindowMagnificationMgr()).thenReturn(
+ mMockWindowMagnificationMgr);
mA11yms = new AccessibilityManagerService(
InstrumentationRegistry.getContext(),
mMockPackageManager,
@@ -117,7 +121,7 @@
mMockSystemActionPerformer,
mMockA11yWindowManager,
mMockA11yDisplayListener,
- mMockWindowMagnificationMgr);
+ mMockMagnificationController);
final AccessibilityUserState userState = new AccessibilityUserState(
mA11yms.getCurrentUserIdLocked(), mMockContext, mA11yms);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
similarity index 85%
rename from services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
rename to services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cd8e39c..3e34f8a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationTransitionControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -62,7 +62,7 @@
* Tests for MagnificationController.
*/
@RunWith(AndroidJUnit4.class)
-public class MagnificationTransitionControllerTest {
+public class MagnificationControllerTest {
private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
private static final Region MAGNIFICATION_REGION = new Region(0, 0, 500, 600);
@@ -75,7 +75,7 @@
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
@Mock private AccessibilityManagerService mService;
- @Mock private MagnificationTransitionController.TransitionCallBack mTransitionCallBack;
+ @Mock private MagnificationController.TransitionCallBack mTransitionCallBack;
@Mock private Context mContext;
@Mock private FullScreenMagnificationController mScreenMagnificationController;
@Captor private ArgumentCaptor<MagnificationAnimationCallback> mCallbackArgumentCaptor;
@@ -83,7 +83,7 @@
private MockWindowMagnificationConnection mMockConnection;
private WindowMagnificationManager mWindowMagnificationManager;
private MockContentResolver mMockResolver;
- private MagnificationTransitionController mMagnificationTransitionController;
+ private MagnificationController mMagnificationController;
@Before
public void setUp() throws Exception {
@@ -95,14 +95,12 @@
Settings.Secure.putFloatForUser(mMockResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, DEFAULT_SCALE,
CURRENT_USER_ID);
- mWindowMagnificationManager = new WindowMagnificationManager(mContext, CURRENT_USER_ID);
- when(mService.getFullScreenMagnificationController()).thenReturn(
- mScreenMagnificationController);
- when(mService.getWindowMagnificationMgr()).thenReturn(mWindowMagnificationManager);
+ mWindowMagnificationManager = Mockito.spy(
+ new WindowMagnificationManager(mContext, CURRENT_USER_ID));
mMockConnection = new MockWindowMagnificationConnection(true);
mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
- mMagnificationTransitionController = new MagnificationTransitionController(mService,
- new Object());
+ mMagnificationController = new MagnificationController(mService, new Object(), mContext,
+ mScreenMagnificationController, mWindowMagnificationManager);
}
@After
@@ -113,7 +111,7 @@
@Test
public void transitionToWindowMode_notMagnifying_doNothing() throws RemoteException {
setMagnificationModeSettings(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -130,7 +128,7 @@
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -147,11 +145,11 @@
public void transitionToWindowMode_disablingWindowMode_enablingWindowWithFormerCenter()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
@@ -166,7 +164,7 @@
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
mMockConnection.invokeCallbacks();
@@ -186,7 +184,7 @@
magnificationBounds.bottom + 100);
setMagnificationEnabled(MODE_WINDOW, magnifiedCenter.x, magnifiedCenter.y);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
mMockConnection.invokeCallbacks();
@@ -202,11 +200,11 @@
public void transitionToFullScreenMode_disablingFullScreen_enableFullScreenWithFormerCenter()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_WINDOW,
mTransitionCallBack);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
@@ -221,7 +219,7 @@
public void interruptDuringTransitionToFullScreenMode_windowMagnifying_notifyTransitionFailed()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationTransitionController.transitionMagnificationModeLocked(TEST_DISPLAY,
+ mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY,
MODE_FULLSCREEN,
mTransitionCallBack);
@@ -237,7 +235,23 @@
verify(mTransitionCallBack).onResult(false);
}
+ @Test
+ public void onDisplayRemoved_notifyAllModules() {
+ mMagnificationController.onDisplayRemoved(TEST_DISPLAY);
+
+ verify(mScreenMagnificationController).onDisplayRemoved(TEST_DISPLAY);
+ verify(mWindowMagnificationManager).onDisplayRemoved(TEST_DISPLAY);
+ }
+
+ @Test
+ public void updateUserIdIfNeeded_AllModulesAvailable_setUserId() {
+ mMagnificationController.updateUserIdIfNeeded(CURRENT_USER_ID);
+
+ verify(mScreenMagnificationController).setUserId(CURRENT_USER_ID);
+ verify(mWindowMagnificationManager).setUserId(CURRENT_USER_ID);
+ }
private void setMagnificationEnabled(int mode) throws RemoteException {
+
setMagnificationEnabled(mode, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 89b0a03..d5be3ed 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -369,6 +369,16 @@
assertEquals(mWindowMagnificationManager.getCenterY(TEST_DISPLAY), 200f);
}
+ @Test
+ public void onDisplayRemoved_enabledOnTestDisplay_disabled() {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, 100f, 200f);
+
+ mWindowMagnificationManager.onDisplayRemoved(TEST_DISPLAY);
+
+ assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 08f558e..1385376 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -54,8 +54,6 @@
private Context mContextSpy;
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
- private HdmiCecController mHdmiCecController;
- private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
private ArcInitiationActionFromAvr mAction;
@@ -78,7 +76,7 @@
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService =
+ HdmiControlService hdmiControlService =
new HdmiControlService(mContextSpy) {
@Override
boolean isPowerStandby() {
@@ -110,7 +108,7 @@
}
};
- mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
protected void setPreferredAddress(int addr) {
}
@@ -118,18 +116,18 @@
mHdmiCecLocalDeviceAudioSystem.init();
Looper looper = mTestLooper.getLooper();
- mHdmiControlService.setIoLooper(looper);
+ hdmiControlService.setIoLooper(looper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
- mHdmiControlService.setCecController(mHdmiCecController);
- mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
- mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
- mHdmiControlService.initPortInfo();
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
+ hdmiControlService.setCecController(hdmiCecController);
+ hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
+ hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
+ hdmiControlService.initPortInfo();
mAction = new ArcInitiationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ hdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
}
@@ -142,7 +140,7 @@
assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
- mHdmiControlService.sendCecCommand(
+ mNativeWrapper.onCecMessage(
HdmiCecMessageBuilder.buildReportArcInitiated(
Constants.ADDR_TV,
Constants.ADDR_AUDIO_SYSTEM));
@@ -174,7 +172,7 @@
assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
- mHdmiControlService.handleCecCommand(HdmiCecMessageBuilder.buildReportArcTerminated(
+ mNativeWrapper.onCecMessage(HdmiCecMessageBuilder.buildReportArcTerminated(
Constants.ADDR_TV,
Constants.ADDR_AUDIO_SYSTEM));
mTestLooper.dispatchAll();
@@ -192,7 +190,7 @@
assertThat(mNativeWrapper.getResultMessages()).contains(initiateArc);
- mHdmiControlService.handleCecCommand(
+ mNativeWrapper.onCecMessage(
HdmiCecMessageBuilder.buildFeatureAbortCommand(
Constants.ADDR_TV,
Constants.ADDR_AUDIO_SYSTEM, Constants.MESSAGE_INITIATE_ARC,
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 4afbbf7..169f885 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -56,8 +56,6 @@
private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
private ArcTerminationActionFromAvr mAction;
- private HdmiCecController mHdmiCecController;
- private HdmiControlService mHdmiControlService;
private FakeNativeWrapper mNativeWrapper;
private TestLooper mTestLooper = new TestLooper();
@@ -79,7 +77,7 @@
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
when(mIPowerManagerMock.isInteractive()).thenReturn(true);
- mHdmiControlService =
+ HdmiControlService hdmiControlService =
new HdmiControlService(mContextSpy) {
@Override
void wakeUp() {
@@ -112,16 +110,16 @@
};
Looper looper = mTestLooper.getLooper();
- mHdmiControlService.setIoLooper(looper);
+ hdmiControlService.setIoLooper(looper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- this.mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
- mHdmiControlService.setCecController(mHdmiCecController);
- mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
- mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
- mHdmiControlService.initPortInfo();
+ HdmiCecController hdmiCecController = HdmiCecController.createWithNativeWrapper(
+ hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
+ hdmiControlService.setCecController(hdmiCecController);
+ hdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(hdmiControlService));
+ hdmiControlService.setMessageValidator(new HdmiCecMessageValidator(hdmiControlService));
+ hdmiControlService.initPortInfo();
- mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService) {
+ mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
protected void setPreferredAddress(int addr) {
}
@@ -130,7 +128,7 @@
mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
mLocalDevices.add(mHdmiCecLocalDeviceAudioSystem);
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ hdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
mTestLooper.dispatchAll();
}
@@ -173,7 +171,7 @@
HdmiCecMessage arcTerminatedResponse = HdmiCecMessageBuilder.buildReportArcTerminated(
Constants.ADDR_TV, Constants.ADDR_AUDIO_SYSTEM);
- mHdmiControlService.handleCecCommand(arcTerminatedResponse);
+ mNativeWrapper.onCecMessage(arcTerminatedResponse);
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 01f0a3d..2c42791 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -16,7 +16,11 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.CecMessage;
+import android.hardware.tv.cec.V1_0.HotplugEvent;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.RemoteException;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
@@ -29,6 +33,8 @@
/** Fake {@link NativeWrapper} useful for testing. */
final class FakeNativeWrapper implements NativeWrapper {
+ private static final String TAG = "FakeNativeWrapper";
+
private final int[] mPollAddressResponse =
new int[] {
SendMessageResult.NACK,
@@ -52,6 +58,7 @@
private final HashMap<Integer, Integer> mMessageSendResult = new HashMap<>();
private int mMyPhysicalAddress = 0;
private HdmiPortInfo[] mHdmiPortInfo = null;
+ private HdmiCecController.HdmiCecCallback mCallback = null;
@Override
public String nativeInit() {
@@ -59,7 +66,9 @@
}
@Override
- public void setCallback(HdmiCecController.HdmiCecCallback callback) {}
+ public void setCallback(HdmiCecController.HdmiCecCallback callback) {
+ this.mCallback = callback;
+ }
@Override
public int nativeSendCecCommand(
@@ -119,6 +128,42 @@
return false;
}
+ public void onCecMessage(HdmiCecMessage hdmiCecMessage) {
+ if (mCallback == null) {
+ return;
+ }
+ CecMessage message = new CecMessage();
+ message.initiator = hdmiCecMessage.getSource();
+ message.destination = hdmiCecMessage.getDestination();
+ ArrayList<Byte> body = new ArrayList<>();
+ body.add((byte) hdmiCecMessage.getOpcode());
+ for (byte param : hdmiCecMessage.getParams()) {
+ body.add(param);
+ }
+ message.body = body;
+ try {
+ mCallback.onCecMessage(message);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending CEC message", e);
+ }
+ }
+
+ public void onHotplugEvent(int port, boolean connected) {
+ if (mCallback == null) {
+ return;
+ }
+
+ HotplugEvent hotplugEvent = new HotplugEvent();
+ hotplugEvent.portId = port;
+ hotplugEvent.connected = connected;
+
+ try {
+ mCallback.onHotplugEvent(hotplugEvent);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error sending hotplug event", e);
+ }
+ }
+
public List<HdmiCecMessage> getResultMessages() {
return new ArrayList<>(mResultMessages);
}
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 c60d5fb..6e7ec2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -70,7 +70,6 @@
}
}
- private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private int mLogicalAddress = 16;
private AllocateAddressCallback mCallback =
@@ -87,10 +86,11 @@
public void SetUp() {
mMyLooper = mTestLooper.getLooper();
mMyLooper = mTestLooper.getLooper();
- mHdmiControlService = new MyHdmiControlService(InstrumentationRegistry.getTargetContext());
+ HdmiControlService hdmiControlService = new MyHdmiControlService(
+ InstrumentationRegistry.getTargetContext());
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, mNativeWrapper, mHdmiControlService.getAtomWriter());
+ hdmiControlService, mNativeWrapper, hdmiControlService.getAtomWriter());
}
/** Tests for {@link HdmiCecController#allocateLogicalAddress} */
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index d160a3f..498ebf4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -39,7 +39,6 @@
import androidx.test.filters.SmallTest;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -370,16 +369,11 @@
assertThat(mStandby).isFalse();
}
- // Playback device does not handle routing control related feature right now
- @Ignore("b/120845532")
@Test
- public void handleSetStreamPath_underCurrentDevice() {
- assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0);
+ public void handleSetStreamPath() {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
- // TODO(amyjojo): Move set and get LocalActivePath to Control Service.
- assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(1);
}
@Test
@@ -786,8 +780,8 @@
@Test
public void handleSetStreamPath_afterHotplug_broadcastsActiveSource() {
- mHdmiControlService.onHotplug(1, false);
- mHdmiControlService.onHotplug(1, true);
+ mNativeWrapper.onHotplugEvent(1, false);
+ mNativeWrapper.onHotplugEvent(1, true);
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
mPlaybackPhysicalAddress);
@@ -803,8 +797,8 @@
@Test
public void handleSetStreamPath_afterHotplug_hasCorrectActiveSource() {
- mHdmiControlService.onHotplug(1, false);
- mHdmiControlService.onHotplug(1, true);
+ mNativeWrapper.onHotplugEvent(1, false);
+ mNativeWrapper.onHotplugEvent(1, true);
HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
mPlaybackPhysicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index c5d9487..c6823eb 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -37,6 +37,7 @@
private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890");
private static final String PHONE_NUMBER = "+1234567890";
private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
+ private static final String PARENT_NOTIFICATION_CHANNEL_ID = "test";
@Test
public void testBuild() {
@@ -46,6 +47,8 @@
.setContactUri(CONTACT_URI)
.setContactPhoneNumber(PHONE_NUMBER)
.setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
+ .setParentNotificationChannelId(PARENT_NOTIFICATION_CHANNEL_ID)
+ .setLastEventTimestamp(100L)
.setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED
| ShortcutInfo.FLAG_CACHED_NOTIFICATIONS)
.setImportant(true)
@@ -62,6 +65,9 @@
assertEquals(CONTACT_URI, conversationInfo.getContactUri());
assertEquals(PHONE_NUMBER, conversationInfo.getContactPhoneNumber());
assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+ assertEquals(PARENT_NOTIFICATION_CHANNEL_ID,
+ conversationInfo.getParentNotificationChannelId());
+ assertEquals(100L, conversationInfo.getLastEventTimestamp());
assertTrue(conversationInfo.isShortcutLongLived());
assertTrue(conversationInfo.isShortcutCachedForNotification());
assertTrue(conversationInfo.isImportant());
@@ -84,6 +90,8 @@
assertNull(conversationInfo.getContactUri());
assertNull(conversationInfo.getContactPhoneNumber());
assertNull(conversationInfo.getNotificationChannelId());
+ assertNull(conversationInfo.getParentNotificationChannelId());
+ assertEquals(0L, conversationInfo.getLastEventTimestamp());
assertFalse(conversationInfo.isShortcutLongLived());
assertFalse(conversationInfo.isShortcutCachedForNotification());
assertFalse(conversationInfo.isImportant());
@@ -103,6 +111,8 @@
.setContactUri(CONTACT_URI)
.setContactPhoneNumber(PHONE_NUMBER)
.setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
+ .setParentNotificationChannelId(PARENT_NOTIFICATION_CHANNEL_ID)
+ .setLastEventTimestamp(100L)
.setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
.setImportant(true)
.setNotificationSilenced(true)
@@ -122,6 +132,8 @@
assertEquals(CONTACT_URI, destination.getContactUri());
assertEquals(PHONE_NUMBER, destination.getContactPhoneNumber());
assertEquals(NOTIFICATION_CHANNEL_ID, destination.getNotificationChannelId());
+ assertEquals(PARENT_NOTIFICATION_CHANNEL_ID, destination.getParentNotificationChannelId());
+ assertEquals(100L, destination.getLastEventTimestamp());
assertTrue(destination.isShortcutLongLived());
assertFalse(destination.isImportant());
assertTrue(destination.isNotificationSilenced());
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 0a6cd51..f37054d 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -29,6 +29,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
@@ -45,6 +46,7 @@
import android.app.NotificationManager;
import android.app.Person;
import android.app.job.JobScheduler;
+import android.app.people.ConversationChannel;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
@@ -112,6 +114,7 @@
private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
private static final String PHONE_NUMBER = "+1234567890";
private static final String NOTIFICATION_CHANNEL_ID = "test : sc";
+ private static final String PARENT_NOTIFICATION_CHANNEL_ID = "test";
private static final long MILLIS_PER_MINUTE = 1000L * 60L;
@Mock private Context mContext;
@@ -133,10 +136,12 @@
private ScheduledExecutorService mExecutorService;
private NotificationChannel mNotificationChannel;
+ private NotificationChannel mParentNotificationChannel;
private DataManager mDataManager;
private CancellationSignal mCancellationSignal;
private ShortcutChangeCallback mShortcutChangeCallback;
private BroadcastReceiver mShutdownBroadcastReceiver;
+ private ShortcutInfo mShortcutInfo;
private TestInjector mInjector;
@Before
@@ -157,6 +162,11 @@
}).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
addLocalServiceMock(NotificationManagerInternal.class, mNotificationManagerInternal);
+ mParentNotificationChannel = new NotificationChannel(
+ PARENT_NOTIFICATION_CHANNEL_ID, "test channel",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ when(mNotificationManagerInternal.getNotificationChannel(anyString(), anyInt(),
+ anyString())).thenReturn(mParentNotificationChannel);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
@@ -199,6 +209,7 @@
when(mStatusBarNotification.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
when(mStatusBarNotification.getPostTime()).thenReturn(System.currentTimeMillis());
when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
+ when(mNotification.getChannelId()).thenReturn(PARENT_NOTIFICATION_CHANNEL_ID);
mNotificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT);
@@ -212,6 +223,13 @@
when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
anyString(), anyInt(), any())).thenReturn(true);
+
+ mShortcutInfo = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ when(mShortcutServiceInternal.getShortcuts(
+ anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
+ anyInt(), anyInt(), anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(mShortcutInfo));
verify(mShortcutServiceInternal).addShortcutChangeCallback(
mShortcutChangeCallbackCaptor.capture());
mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
@@ -417,29 +435,28 @@
List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
Event.NOTIFICATION_EVENT_TYPES);
assertEquals(1, activeNotificationOpenTimeSlots.size());
- verify(mShortcutServiceInternal).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
- eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
@Test
- public void testNotificationDismissed() {
+ public void testUncacheShortcutsWhenNotificationsDismissed() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-
- ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
- buildPerson());
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
- // Post one notification.
- shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mDataManager.addOrUpdateConversationInfo(shortcut);
- listenerService.onNotificationPosted(mStatusBarNotification);
+ // The cached conversations are above the limit because every conversation has active
+ // notifications. To uncache one of them, the notifications for that conversation need to
+ // be dismissed.
+ for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
+ String shortcutId = TEST_SHORTCUT_ID + i;
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ when(mNotification.getShortcutId()).thenReturn(shortcutId);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ }
- // Post another notification.
+ // Post another notification for the last conversation.
listenerService.onNotificationPosted(mStatusBarNotification);
// Removing one of the two notifications does not un-cache the shortcut.
@@ -452,13 +469,12 @@
listenerService.onNotificationRemoved(mStatusBarNotification, null,
NotificationListenerService.REASON_CANCEL_ALL);
verify(mShortcutServiceInternal).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
+ anyInt(), any(), eq(TEST_PKG_NAME), anyList(), eq(USER_ID_PRIMARY),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
@Test
- public void testShortcutNotUncachedIfNotificationChannelCreated() {
+ public void testConversationIsNotRecentIfCustomized() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
@@ -472,15 +488,12 @@
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
+ assertEquals(1, mDataManager.getRecentConversations(USER_ID_PRIMARY).size());
+
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
- listenerService.onNotificationRemoved(mStatusBarNotification, null,
- NotificationListenerService.REASON_CANCEL_ALL);
- verify(mShortcutServiceInternal, never()).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
- eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ assertTrue(mDataManager.getRecentConversations(USER_ID_PRIMARY).isEmpty());
}
@Test
@@ -561,53 +574,6 @@
}
@Test
- public void testUncacheShortcutWhenShutdown() {
- mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-
- ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
- buildPerson());
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
- NotificationListenerService listenerService =
- mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
-
- listenerService.onNotificationPosted(mStatusBarNotification);
- shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
- mShutdownBroadcastReceiver.onReceive(mContext, new Intent());
- verify(mShortcutServiceInternal).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
- eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
- }
-
- @Test
- public void testDoNotUncacheShortcutWhenShutdownIfNotificationChannelCreated() {
- mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-
- ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
- buildPerson());
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
- NotificationListenerService listenerService =
- mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
-
- listenerService.onNotificationPosted(mStatusBarNotification);
- shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mDataManager.addOrUpdateConversationInfo(shortcut);
-
- listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
- mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
-
- mShutdownBroadcastReceiver.onReceive(mContext, new Intent());
- verify(mShortcutServiceInternal, never()).uncacheShortcuts(
- anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
- eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
- }
-
- @Test
public void testShortcutAddedOrUpdated() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
@@ -769,20 +735,57 @@
}
@Test
- public void testPruneInactiveCachedShortcuts() {
+ public void testDoNotUncacheShortcutWithActiveNotifications() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
- ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
- buildPerson());
- shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
- mDataManager.addOrUpdateConversationInfo(shortcut);
+ for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
+ String shortcutId = TEST_SHORTCUT_ID + i;
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ when(mNotification.getShortcutId()).thenReturn(shortcutId);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ }
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+ verify(mShortcutServiceInternal, never()).uncacheShortcuts(
+ anyInt(), anyString(), anyString(), anyList(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void testUncacheOldestCachedShortcut() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+ for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
+ String shortcutId = TEST_SHORTCUT_ID + i;
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ when(mNotification.getShortcutId()).thenReturn(shortcutId);
+ when(mStatusBarNotification.getPostTime()).thenReturn(100L + i);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationRemoved(mStatusBarNotification, null,
+ NotificationListenerService.REASON_CANCEL);
+ }
+
+ // Only the shortcut #0 is uncached, all the others are not.
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME),
- eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY),
+ eq(Collections.singletonList(TEST_SHORTCUT_ID + 0)), eq(USER_ID_PRIMARY),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
+ verify(mShortcutServiceInternal, never()).uncacheShortcuts(
+ anyInt(), anyString(), anyString(),
+ eq(Collections.singletonList(TEST_SHORTCUT_ID + i)), anyInt(),
+ eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ }
}
@Test
@@ -812,6 +815,148 @@
assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
}
+ @Test
+ public void testGetRecentConversations() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
+ assertEquals(1, result.size());
+ assertEquals(shortcut.getId(), result.get(0).getShortcutInfo().getId());
+ assertEquals(mParentNotificationChannel.getId(),
+ result.get(0).getParentNotificationChannel().getId());
+ assertEquals(mStatusBarNotification.getPostTime(), result.get(0).getLastEventTimestamp());
+ assertTrue(result.get(0).hasActiveNotifications());
+ }
+
+ @Test
+ public void testGetLastInteraction() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ assertEquals(mStatusBarNotification.getPostTime(),
+ mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID));
+ assertEquals(0L,
+ mDataManager.getLastInteraction("not_test_pkg", USER_ID_PRIMARY, TEST_SHORTCUT_ID));
+ assertEquals(0L,
+ mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_PRIMARY_MANAGED,
+ TEST_SHORTCUT_ID));
+ assertEquals(0L,
+ mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_SECONDARY,
+ TEST_SHORTCUT_ID));
+ }
+
+ @Test
+ public void testNonCachedShortcutNotInRecentList() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY_MANAGED,
+ TEST_SHORTCUT_ID, buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void testCustomizedConversationNotInRecentList() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ // Post a notification and customize the notification settings.
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+ mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+ List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
+ assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void testRemoveRecentConversation() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationRemoved(mStatusBarNotification, null,
+ NotificationListenerService.REASON_CANCEL);
+ mDataManager.removeRecentConversation(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ USER_ID_PRIMARY);
+
+ verify(mShortcutServiceInternal).uncacheShortcuts(
+ anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
+ eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ }
+
+ @Test
+ public void testRemoveAllRecentConversations() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut1 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "1",
+ buildPerson());
+ shortcut1.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut1);
+
+ ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "2",
+ buildPerson());
+ shortcut2.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut2);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+ // Post a notification and then dismiss it for conversation #1.
+ when(mNotification.getShortcutId()).thenReturn("1");
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ listenerService.onNotificationRemoved(mStatusBarNotification, null,
+ NotificationListenerService.REASON_CANCEL);
+
+ // Post a notification for conversation #2, but don't dismiss it. Its shortcut won't be
+ // uncached when removeAllRecentConversations() is called.
+ when(mNotification.getShortcutId()).thenReturn("2");
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ mDataManager.removeAllRecentConversations(USER_ID_PRIMARY);
+
+ verify(mShortcutServiceInternal).uncacheShortcuts(
+ anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList("1")),
+ eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ verify(mShortcutServiceInternal, never()).uncacheShortcuts(
+ anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList("2")),
+ eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
+ }
+
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 419fb14..6febae0 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -80,6 +80,7 @@
import com.android.server.lights.LightsManager;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.power.PowerManagerService.BatteryReceiver;
+import com.android.server.power.PowerManagerService.BinderService;
import com.android.server.power.PowerManagerService.Injector;
import com.android.server.power.PowerManagerService.NativeWrapper;
import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
@@ -179,6 +180,7 @@
when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
+ when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
mDisplayPowerRequest = new DisplayPowerRequest();
addLocalServiceMock(LightsManager.class, mLightsManagerMock);
@@ -983,6 +985,74 @@
}
@Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_ambientDisplayUnavailable()
+ throws Exception {
+ createService();
+ when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(false);
+
+ BinderService service = mService.getBinderServiceInstance();
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", Binder.getCallingUid()))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_default()
+ throws Exception {
+ createService();
+
+ BinderService service = mService.getBinderServiceInstance();
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", Binder.getCallingUid()))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_suppressedByCallingApp()
+ throws Exception {
+ createService();
+ BinderService service = mService.getBinderServiceInstance();
+ service.suppressAmbientDisplay("test", true);
+
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", Binder.getCallingUid()))
+ .isTrue();
+ // Check that isAmbientDisplaySuppressedForTokenByApp doesn't return true for another app.
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", /* appUid= */ 123))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_notSuppressedByCallingApp()
+ throws Exception {
+ createService();
+ BinderService service = mService.getBinderServiceInstance();
+ service.suppressAmbientDisplay("test", false);
+
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", Binder.getCallingUid()))
+ .isFalse();
+ // Check that isAmbientDisplaySuppressedForTokenByApp doesn't return true for another app.
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test", /* appUid= */ 123))
+ .isFalse();
+ }
+
+ @Test
+ public void testIsAmbientDisplaySuppressedForTokenByApp_multipleTokensSuppressedByCallingApp()
+ throws Exception {
+ createService();
+ BinderService service = mService.getBinderServiceInstance();
+ service.suppressAmbientDisplay("test1", true);
+ service.suppressAmbientDisplay("test2", true);
+
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test1", Binder.getCallingUid()))
+ .isTrue();
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test2", Binder.getCallingUid()))
+ .isTrue();
+ // Check that isAmbientDisplaySuppressedForTokenByApp doesn't return true for another app.
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test1", /* appUid= */ 123))
+ .isFalse();
+ assertThat(service.isAmbientDisplaySuppressedForTokenByApp("test2", /* appUid= */ 123))
+ .isFalse();
+ }
+
+ @Test
public void testSetPowerBoost_redirectsCallToNativeWrapper() {
createService();
mService.systemReady(null);
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 f10cab8..3af873d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1522,7 +1522,7 @@
try {
// Return error to skip unnecessary operation.
doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay(
- any() /* window */, anyInt() /* seq */, any() /* attrs */,
+ any() /* window */, any() /* attrs */,
anyInt() /* viewVisibility */, anyInt() /* displayId */, any() /* outFrame */,
any() /* outContentInsets */, any() /* outStableInsets */,
any() /* outDisplayCutout */, any() /* outInputChannel */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index ca739c0..91cfd4e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -56,4 +56,12 @@
mImeProvider.scheduleShowImePostLayout(appWin);
assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame());
}
+
+ @Test
+ public void testInputMethodInputTargetCanShowIme() {
+ WindowState target = createWindow(null, TYPE_APPLICATION, "app");
+ mDisplayContent.mInputMethodTarget = target;
+ mImeProvider.scheduleShowImePostLayout(target);
+ assertTrue(mImeProvider.isImeTargetFromDisplayContentAndImeSame());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 13f04d2..2efd4b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -117,7 +117,6 @@
assertEquals(win.mActivityRecord.getPrefixOrderIndex(), app.prefixOrderIndex);
assertEquals(win.mActivityRecord.getTask().mTaskId, app.taskId);
assertEquals(mMockLeash, app.leash);
- assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(mMockLeash, app.position.x, app.position.y);
verify(mMockTransaction).setWindowCrop(mMockLeash, 100, 50);
@@ -274,7 +273,6 @@
assertEquals(new Rect(0, 0, 200, 200), app.startBounds);
assertEquals(mMockLeash, app.leash);
assertEquals(mMockThumbnailLeash, app.startLeash);
- assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(
mMockLeash, app.startBounds.left, app.startBounds.top);
@@ -325,7 +323,6 @@
assertEquals(new Rect(50, 100, 150, 150), app.startBounds);
assertEquals(mMockLeash, app.leash);
assertEquals(mMockThumbnailLeash, app.startLeash);
- assertEquals(win.mWinAnimator.mLastClipRect, app.clipRect);
assertEquals(false, app.isTranslucent);
verify(mMockTransaction).setPosition(
mMockLeash, app.startBounds.left, app.startBounds.top);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 3053fe6..cc8b2a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -173,6 +173,35 @@
}
@Test
+ public void testTaskLayerRank() {
+ final Task rootTask = new TaskBuilder(mSupervisor).build();
+ final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build();
+ new ActivityBuilder(mAtm).setStack(task1).build().mVisibleRequested = true;
+ // RootWindowContainer#invalidateTaskLayers should post to update.
+ waitHandlerIdle(mWm.mH);
+
+ assertEquals(1, task1.mLayerRank);
+ // Only tasks that directly contain activities have a ranking.
+ assertEquals(Task.LAYER_RANK_INVISIBLE, rootTask.mLayerRank);
+
+ final Task task2 = new TaskBuilder(mSupervisor).build();
+ new ActivityBuilder(mAtm).setStack(task2).build().mVisibleRequested = true;
+ waitHandlerIdle(mWm.mH);
+
+ // Note that ensureActivitiesVisible is disabled in SystemServicesTestRule, so both the
+ // activities have the visible rank.
+ assertEquals(2, task1.mLayerRank);
+ // The task2 is the top task, so it has a lower rank as a higher priority oom score.
+ assertEquals(1, task2.mLayerRank);
+
+ task2.moveToBack("test", null /* task */);
+ waitHandlerIdle(mWm.mH);
+
+ assertEquals(1, task1.mLayerRank);
+ assertEquals(2, task2.mLayerRank);
+ }
+
+ @Test
public void testForceStopPackage() {
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
final ActivityRecord activity = task.getTopMostActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index d37f3f4..ea12233 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -94,11 +94,6 @@
}
@Override
- public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, int localValue,
- int localChanges) throws RemoteException {
- }
-
- @Override
public void dispatchWindowShown() throws RemoteException {
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index ca3626d..0cf63f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -115,12 +115,6 @@
expectedRect.bottom);
}
- private void assertPolicyCrop(WindowState w, int left, int top, int right, int bottom) {
- Rect policyCrop = new Rect();
- w.calculatePolicyCrop(policyCrop);
- assertRect(policyCrop, left, top, right, bottom);
- }
-
@Test
public void testLayoutInFullscreenTaskInsets() {
// fullscreen task doesn't use bounds for computeFrame
@@ -335,12 +329,10 @@
final WindowFrames windowFrames = w.getWindowFrames();
windowFrames.setFrames(pf, df, cf, vf, dcf, sf);
w.computeFrame();
- assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
windowFrames.mDecorFrame.setEmpty();
// Likewise with no decor frame we would get no crop
w.computeFrame();
- assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
// Now we set up a window which doesn't fill the entire decor frame.
// Normally it would be cropped to it's frame but in the case of docked resizing
@@ -355,16 +347,7 @@
w.mRequestedHeight = logicalHeight / 2;
w.computeFrame();
- // Normally the crop is shrunk from the decor frame
- // to the computed window frame.
- assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2);
-
doReturn(true).when(w).isDockedResizing();
- // But if we are docked resizing it won't be, however we will still be
- // shrunk to the decor frame and the display.
- assertPolicyCrop(w, 0, 0,
- Math.min(pf.width(), displayInfo.logicalWidth),
- Math.min(pf.height(), displayInfo.logicalHeight));
}
@Test
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 38c7531..e50c009 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -277,6 +277,69 @@
mWpc.getConfiguration().seq, globalSeq);
}
+ @Test
+ public void testComputeOomAdjFromActivities() {
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setCreateTask(true)
+ .setUseProcess(mWpc)
+ .build();
+ activity.mVisibleRequested = true;
+ final int[] callbackResult = { 0 };
+ final int visible = 1;
+ final int paused = 2;
+ final int stopping = 4;
+ final int other = 8;
+ final WindowProcessController.ComputeOomAdjCallback callback =
+ new WindowProcessController.ComputeOomAdjCallback() {
+ @Override
+ public void onVisibleActivity() {
+ callbackResult[0] |= visible;
+ }
+
+ @Override
+ public void onPausedActivity() {
+ callbackResult[0] |= paused;
+ }
+
+ @Override
+ public void onStoppingActivity(boolean finishing) {
+ callbackResult[0] |= stopping;
+ }
+
+ @Override
+ public void onOtherActivity() {
+ callbackResult[0] |= other;
+ }
+ };
+
+ // onStartActivity should refresh the state immediately.
+ mWpc.onStartActivity(0 /* topProcessState */, activity.info);
+ assertEquals(1 /* minTaskLayer */, mWpc.computeOomAdjFromActivities(callback));
+ assertEquals(visible, callbackResult[0]);
+
+ // The oom state will be updated in handler from activity state change.
+ callbackResult[0] = 0;
+ activity.mVisibleRequested = false;
+ activity.setState(Task.ActivityState.PAUSED, "test");
+ waitHandlerIdle(mAtm.mH);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(paused, callbackResult[0]);
+
+ // updateProcessInfo with updateOomAdj=true should refresh the state immediately.
+ callbackResult[0] = 0;
+ activity.setState(Task.ActivityState.STOPPING, "test");
+ mWpc.updateProcessInfo(false /* updateServiceConnectionActivities */,
+ true /* activityChange */, true /* updateOomAdj */, false /* addPendingTopUid */);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(stopping, callbackResult[0]);
+
+ callbackResult[0] = 0;
+ activity.setState(Task.ActivityState.STOPPED, "test");
+ waitHandlerIdle(mAtm.mH);
+ mWpc.computeOomAdjFromActivities(callback);
+ assertEquals(other, callbackResult[0]);
+ }
+
private TestDisplayContent createTestDisplayContentInContainer() {
return new TestDisplayContent.Builder(mAtm, 1000, 1500).build();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 7daddd8..986807e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -360,7 +360,7 @@
attrs.setTitle(name);
final WindowState w = new WindowState(service, session, iWindow, token, parent,
- OP_NONE, 0, attrs, VISIBLE, ownerId, userId,
+ OP_NONE, attrs, VISIBLE, ownerId, userId,
ownerCanAddInternalSystemWindow,
powerManagerWrapper);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
@@ -1088,7 +1088,7 @@
TestWindowState(WindowManagerService service, Session session, IWindow window,
WindowManager.LayoutParams attrs, WindowToken token) {
- super(service, session, window, token, null, OP_NONE, 0, attrs, 0, 0, 0,
+ super(service, session, window, token, null, OP_NONE, attrs, 0, 0, 0,
false /* ownerCanAddInternalSystemWindow */);
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index f7fe1ba..1472a4a 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,7 +16,6 @@
package android.telecom;
-import android.annotation.SystemApi;
import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
@@ -97,10 +96,7 @@
*
* This reason code is only used for communication between a {@link ConnectionService} and
* Telecom and should not be surfaced to the user.
- *
- * @hide
*/
- @SystemApi
public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
/**
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index c20e5ad..4a3561b 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -28,6 +28,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -52,8 +53,17 @@
*
* This is an extras key set via {@link Builder#setExtras} which determines the order in which
* {@link PhoneAccount}s from the same {@link ConnectionService} are sorted. The accounts
- * are sorted by this key via standard lexicographical order, and this ordering is used to
+ * are sorted by this key via standard lexicographical order, (as implemented in
+ * {@link String#compareTo}), and this ordering is used to
* determine priority when a call can be placed via multiple accounts.
+ *
+ * When multiple {@link PhoneAccount}s are supplied with the same sort order key, no ordering is
+ * guaranteed between those {@link PhoneAccount}s. Additionally, no ordering is guaranteed
+ * between {@link PhoneAccount}s that do not supply this extra, and all such accounts
+ * will be sorted after the accounts that do supply this extra.
+ *
+ * An example of a sort order key is slot index (see {@link TelephonyManager#getSlotIndex()}),
+ * which is the one used by the cell Telephony stack.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 47bc566..8261b53 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -79,6 +79,30 @@
*/
public static final int SERVICE_CLASS_VOICE = ImsSsData.SERVICE_CLASS_VOICE;
+ /**
+ * Only send USSD over IMS while CS is out of service, otherwise send USSD over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_CS_PREFERRED = 0;
+
+ /**
+ * Send USSD over IMS or CS while IMS is out of service or silent redial over CS if needed.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_IMS_PREFERRED = 1;
+
+ /**
+ * Only send USSD over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_CS_ONLY = 2;
+
+ /**
+ * Only send USSD over IMS and disallow silent redial over CS.
+ * {@link #KEY_CARRIER_USSD_METHOD_INT}
+ */
+ public static final int USSD_OVER_IMS_ONLY = 3;
+
private final Context mContext;
/**
@@ -584,6 +608,20 @@
public static final String KEY_CARRIER_VT_AVAILABLE_BOOL = "carrier_vt_available_bool";
/**
+ * Specify the method of selection for UE sending USSD requests. The default value is
+ * {@link #USSD_OVER_CS_PREFERRED}.
+ * <p> Available options:
+ * <ul>
+ * <li>0: {@link #USSD_OVER_CS_PREFERRED} </li>
+ * <li>1: {@link #USSD_OVER_IMS_PREFERRED} </li>
+ * <li>2: {@link #USSD_OVER_CS_ONLY} </li>
+ * <li>3: {@link #USSD_OVER_IMS_ONLY} </li>
+ * </ul>
+ */
+ public static final String KEY_CARRIER_USSD_METHOD_INT =
+ "carrier_ussd_method_int";
+
+ /**
* Flag specifying whether to show an alert dialog for 5G disable when the user disables VoLTE.
* By default this value is {@code false}.
*
@@ -3932,6 +3970,16 @@
public static final String KEY_DEFAULT_PREFERRED_APN_NAME_STRING =
"default_preferred_apn_name_string";
+ /**
+ * For Android 11, provide a temporary solution for OEMs to use the lower of the two MTU values
+ * for IPv4 and IPv6 if both are sent.
+ * TODO: remove in later release
+ *
+ * @hide
+ */
+ public static final String KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED =
+ "use_lower_mtu_value_if_both_received";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3953,6 +4001,7 @@
sDefaults.putBoolean(KEY_CARRIER_SETTINGS_ENABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VT_AVAILABLE_BOOL, false);
+ sDefaults.putInt(KEY_CARRIER_USSD_METHOD_INT, USSD_OVER_CS_PREFERRED);
sDefaults.putBoolean(KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL, false);
sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_MERGING_RTT_CALLS_BOOL, false);
@@ -4473,6 +4522,7 @@
sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false);
sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, "");
+ sDefaults.putBoolean(KEY_USE_LOWER_MTU_VALUE_IF_BOTH_RECEIVED, false);
}
/**
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index cdf7351..8f5ec36 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -29,7 +29,10 @@
/**
* Abstract class that represents the location of the device. {@more}
+ *
+ * @deprecated use {@link android.telephony.CellIdentity CellIdentity}.
*/
+@Deprecated
public abstract class CellLocation {
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a82d988..7a0e1cd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -85,8 +85,6 @@
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.aidl.IImsConfig;
-import android.telephony.ims.aidl.IImsMmTelFeature;
-import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -94,7 +92,6 @@
import android.util.Log;
import android.util.Pair;
-import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CellNetworkScanResult;
@@ -2807,7 +2804,11 @@
/** Current network is LTE_CA {@hide} */
@UnsupportedAppUsage
public static final int NETWORK_TYPE_LTE_CA = TelephonyProtoEnums.NETWORK_TYPE_LTE_CA; // = 19.
- /** Current network is NR(New Radio) 5G. */
+ /**
+ * Current network is NR (New Radio) 5G.
+ * This will only be returned for 5G SA.
+ * For 5G NSA, the network type will be {@link #NETWORK_TYPE_LTE}.
+ */
public static final int NETWORK_TYPE_NR = TelephonyProtoEnums.NETWORK_TYPE_NR; // 20.
private static final @NetworkType int[] NETWORK_TYPES = {
@@ -7380,80 +7381,6 @@
}
/**
- * Returns the {@link IImsMmTelFeature} that corresponds to the given slot Id and MMTel
- * feature or {@link null} if the service is not available. If an MMTelFeature is available, the
- * {@link IImsServiceFeatureCallback} callback is registered as a listener for feature updates.
- * @param slotIndex The SIM slot that we are requesting the {@link IImsMmTelFeature} for.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * ImsServiceController.
- * @return {@link IImsMmTelFeature} interface for the feature specified or {@code null} if
- * it is unavailable.
- * @hide
- */
- public @Nullable IImsMmTelFeature getImsMmTelFeatureAndListen(int slotIndex,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.getMmTelFeatureAndListen(slotIndex, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "getImsMmTelFeatureAndListen, RemoteException: "
- + e.getMessage());
- }
- return null;
- }
-
- /**
- * Returns the {@link IImsRcsFeature} that corresponds to the given slot Id and RCS
- * feature for emergency calling or {@link null} if the service is not available. If an
- * RcsFeature is available, the {@link IImsServiceFeatureCallback} callback is registered as a
- * listener for feature updates.
- * @param slotIndex The SIM slot that we are requesting the {@link IImsRcsFeature} for.
- * @param callback Listener that will send updates to ImsManager when there are updates to
- * ImsServiceController.
- * @return {@link IImsRcsFeature} interface for the feature specified or {@code null} if
- * it is unavailable.
- * @hide
- */
- public @Nullable IImsRcsFeature getImsRcsFeatureAndListen(int slotIndex,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- return telephony.getRcsFeatureAndListen(slotIndex, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "getImsRcsFeatureAndListen, RemoteException: "
- + e.getMessage());
- }
- return null;
- }
-
- /**
- * Unregister a IImsServiceFeatureCallback previously associated with an ImsFeature through
- * {@link #getImsMmTelFeatureAndListen(int, IImsServiceFeatureCallback)} or
- * {@link #getImsRcsFeatureAndListen(int, IImsServiceFeatureCallback)}.
- * @param slotIndex The SIM slot associated with the callback.
- * @param featureType The {@link android.telephony.ims.feature.ImsFeature.FeatureType}
- * associated with the callback.
- * @param callback The callback to be unregistered.
- * @hide
- */
- public void unregisterImsFeatureCallback(int slotIndex, int featureType,
- IImsServiceFeatureCallback callback) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null) {
- telephony.unregisterImsFeatureCallback(slotIndex, featureType, callback);
- }
- } catch (RemoteException e) {
- Rlog.e(TAG, "unregisterImsFeatureCallback, RemoteException: "
- + e.getMessage());
- }
- }
-
- /**
* @return the {@IImsRegistration} interface that corresponds with the slot index and feature.
* @param slotIndex The SIM slot corresponding to the ImsService ImsRegistration is active for.
* @param feature An integer indicating the feature that we wish to get the ImsRegistration for.
diff --git a/telephony/java/android/telephony/cdma/CdmaCellLocation.java b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
index 9bc39a0..d808cab 100644
--- a/telephony/java/android/telephony/cdma/CdmaCellLocation.java
+++ b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
@@ -23,7 +23,10 @@
/**
* Represents the cell location on a CDMA phone.
+ *
+ * @deprecated use {@link android.telephony.CellIdentity CellIdentity}.
*/
+@Deprecated
public class CdmaCellLocation extends CellLocation {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private int mBaseStationId = -1;
diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java
index bc8ee1d..2eee4ce 100644
--- a/telephony/java/android/telephony/gsm/GsmCellLocation.java
+++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java
@@ -23,7 +23,10 @@
/**
* Represents the cell location on a GSM phone.
+ *
+ * @deprecated use {@link android.telephony.CellIdentity CellIdentity}.
*/
+@Deprecated
public class GsmCellLocation extends CellLocation {
private int mLac;
private int mCid;
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index f6c14e6..ee2fce7 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -59,6 +59,7 @@
* manager.
*/
public class ImsMmTelManager implements RegistrationManager {
+ private static final String TAG = "ImsMmTelManager";
/**
* @hide
@@ -809,7 +810,7 @@
}
try {
- getITelephony().isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
+ iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
@Override
public void accept(int result) {
executor.execute(() -> callback.accept(result == 1));
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index da7311c..8a05bdf 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -16,6 +16,7 @@
package android.telephony.ims;
+import android.annotation.LongDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Service;
@@ -41,6 +42,11 @@
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
* ImsService must register the service in their AndroidManifest to be detected by the framework.
@@ -98,6 +104,32 @@
private static final String LOG_TAG = "ImsService";
/**
+ * This ImsService supports the capability to place emergency calls over MMTEL.
+ * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
+ * adding other capabilities in a central location, so track this capability here as well.
+ */
+ public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
+
+ /**
+ * @hide
+ */
+ @LongDef(flag = true,
+ prefix = "CAPABILITY_",
+ value = {
+ CAPABILITY_EMERGENCY_OVER_MMTEL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsServiceCapability {}
+
+ /**
+ * Used for logging purposes, see {@link #getCapabilitiesString(long)}
+ * @hide
+ */
+ private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
+ put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
+ }};
+
+ /**
* The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
* @hide
*/
@@ -409,4 +441,30 @@
public ImsRegistrationImplBase getRegistration(int slotId) {
return new ImsRegistrationImplBase();
}
+
+ /**
+ * @return A string representation of the ImsService capabilties for logging.
+ * @hide
+ */
+ public static String getCapabilitiesString(@ImsServiceCapability long caps) {
+ StringBuffer result = new StringBuffer();
+ result.append("capabilities={ ");
+ // filter incrementally fills 0s from left to right. This is used to keep filtering out
+ // more bits in the long until the remaining leftmost bits are all zero.
+ long filter = 0xFFFFFFFFFFFFFFFFL;
+ // position of iterator to potentially print capability.
+ long i = 0;
+ while ((caps & filter) != 0 && i <= 63) {
+ long bitToCheck = (1L << i);
+ if ((caps & bitToCheck) != 0) {
+ result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?"));
+ result.append(" ");
+ }
+ // shift left by one and fill in another 1 on the leftmost bit.
+ filter <<= 1;
+ i++;
+ }
+ result.append("}");
+ return result.toString();
+ }
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 9e46142..d012703b 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -22,6 +22,7 @@
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
import android.telephony.ims.aidl.IImsRegistrationCallback;
+import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.telephony.IIntegerConsumer;
/**
@@ -50,4 +51,9 @@
void setUceSettingEnabled(int subId, boolean isEnabled);
void registerUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
void unregisterUcePublishStateCallback(int subId, IRcsUcePublishStateCallback c);
+
+ // Internal commands that should not be made public
+ void registerRcsFeatureCallback(int slotId, in IImsServiceFeatureCallback callback,
+ boolean oneShot);
+ void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
}
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.aidl b/telephony/java/com/android/ims/ImsFeatureContainer.aidl
new file mode 100644
index 0000000..9706f20
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+parcelable ImsFeatureContainer;
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java
new file mode 100644
index 0000000..b259679
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsFeatureContainer.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.ims;
+
+import android.annotation.NonNull;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.feature.ImsFeature;
+
+import java.util.Objects;
+
+/**
+ * Contains an IBinder linking to the appropriate ImsFeature as well as the associated
+ * interfaces.
+ * @hide
+ */
+public final class ImsFeatureContainer implements Parcelable {
+ /**
+ * ImsFeature that is being tracked.
+ */
+ public final IBinder imsFeature;
+
+ /**
+ * IImsConfig interface that should be associated with the ImsFeature.
+ */
+ public final android.telephony.ims.aidl.IImsConfig imsConfig;
+
+ /**
+ * IImsRegistration interface that should be associated with this ImsFeature.
+ */
+ public final IImsRegistration imsRegistration;
+
+ /**
+ * State of the feature that is being tracked.
+ */
+ private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE;
+
+ /**
+ * Capabilities of this ImsService.
+ */
+ private @ImsService.ImsServiceCapability long mCapabilities;
+ /**
+ * Contains the ImsFeature IBinder as well as the ImsService interfaces associated with
+ * that feature.
+ * @param iFace IBinder connection to the ImsFeature.
+ * @param iConfig IImsConfig interface associated with the ImsFeature.
+ * @param iReg IImsRegistration interface associated with the ImsFeature
+ * @param initialCaps The initial capabilities that the ImsService supports.
+ */
+ public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig,
+ @NonNull IImsRegistration iReg, long initialCaps) {
+ imsFeature = iFace;
+ imsConfig = iConfig;
+ imsRegistration = iReg;
+ mCapabilities = initialCaps;
+ }
+
+ /**
+ * Create an ImsFeatureContainer from a Parcel.
+ */
+ private ImsFeatureContainer(Parcel in) {
+ imsFeature = in.readStrongBinder();
+ imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder());
+ imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder());
+ mState = in.readInt();
+ mCapabilities = in.readLong();
+ }
+
+ /**
+ * @return the capabilties that are associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public @ImsService.ImsServiceCapability long getCapabilities() {
+ return mCapabilities;
+ }
+
+ /**
+ * Update the capabilities that are associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public void setCapabilities(@ImsService.ImsServiceCapability long caps) {
+ mCapabilities = caps;
+ }
+
+ /**
+ * @return The state of the ImsFeature.
+ */
+ public @ImsFeature.ImsState int getState() {
+ return mState;
+ }
+
+ /**
+ * Set the state that is associated with the ImsService that this ImsFeature
+ * belongs to.
+ */
+ public void setState(@ImsFeature.ImsState int state) {
+ mState = state;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ImsFeatureContainer that = (ImsFeatureContainer) o;
+ return imsFeature.equals(that.imsFeature) &&
+ imsConfig.equals(that.imsConfig) &&
+ imsRegistration.equals(that.imsRegistration) &&
+ mState == that.getState() &&
+ mCapabilities == that.getCapabilities();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities);
+ }
+
+ @Override
+ public String toString() {
+ return "FeatureContainer{" +
+ "imsFeature=" + imsFeature +
+ ", imsConfig=" + imsConfig +
+ ", imsRegistration=" + imsRegistration +
+ ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) +
+ ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) +
+ '}';
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(imsFeature);
+ dest.writeStrongInterface(imsConfig);
+ dest.writeStrongInterface(imsRegistration);
+ dest.writeInt(mState);
+ dest.writeLong(mCapabilities);
+ }
+
+
+ public static final Creator<ImsFeatureContainer> CREATOR = new Creator<ImsFeatureContainer>() {
+ @Override
+ public ImsFeatureContainer createFromParcel(Parcel source) {
+ return new ImsFeatureContainer(source);
+ }
+
+ @Override
+ public ImsFeatureContainer[] newArray(int size) {
+ return new ImsFeatureContainer[size];
+ }
+ };
+}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
index 9a9cf53..f5f67bd 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl
@@ -16,13 +16,18 @@
package com.android.ims.internal;
+import com.android.ims.ImsFeatureContainer;
/**
- * Interface from ImsResolver to ImsServiceProxy in ImsManager.
- * Callback to ImsManager when a feature changes in the ImsServiceController.
+ * Interface from ImsResolver to FeatureConnections.
+ * Callback to FeatureConnections when a feature's status changes.
* {@hide}
*/
oneway interface IImsServiceFeatureCallback {
- void imsFeatureCreated(int slotId, int feature);
- void imsFeatureRemoved(int slotId, int feature);
- void imsStatusChanged(int slotId, int feature, int status);
+ void imsFeatureCreated(in ImsFeatureContainer feature);
+ // Reason defined in FeatureConnector.UnavailableReason
+ void imsFeatureRemoved(int reason);
+ // Status defined in ImsFeature.ImsState.
+ void imsStatusChanged(int status);
+ //Capabilities defined in ImsService.ImsServiceCapability
+ void updateCapabilities(long capabilities);
}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 02a74ba..ef5078d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -829,22 +829,15 @@
* as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback
* interface.
*/
- IImsMmTelFeature getMmTelFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
-
- /**
- * Get IImsRcsFeature binder from ImsResolver that corresponds to the subId and RCS feature
- * as well as registering the RcsFeature for callbacks using the IImsServiceFeatureCallback
- * interface.
- */
- IImsRcsFeature getRcsFeatureAndListen(int slotId, in IImsServiceFeatureCallback callback);
+ void registerMmTelFeatureCallback(int slotId, in IImsServiceFeatureCallback callback,
+ boolean oneShot);
/**
* Unregister a callback that was previously registered through
- * {@link #getMmTelFeatureAndListen} or {@link #getRcsFeatureAndListen}. This should always be
- * called when the callback is no longer being used.
+ * {@link #registerMmTelFeatureCallback}. This should always be called when the callback is no
+ * longer being used.
*/
- void unregisterImsFeatureCallback(int slotId, int featureType,
- in IImsServiceFeatureCallback callback);
+ void unregisterImsFeatureCallback(in IImsServiceFeatureCallback callback);
/**
* Returns the IImsRegistration associated with the slot and feature specified.
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 05a59ef..9a2def9 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -745,7 +745,7 @@
</activity>
<activity android:name="BlurActivity"
- android:label="Shaders/Blur"
+ android:label="RenderEffect/Blur"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/tests/HwAccelerationTest/res/layout/image_filter_activity.xml b/tests/HwAccelerationTest/res/layout/image_filter_activity.xml
new file mode 100644
index 0000000..a0ee67a
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/image_filter_activity.xml
@@ -0,0 +1,28 @@
+<?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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/image_filter_test_view"
+ android:background="#FF0000"
+ android:layout_width="200dp"
+ android:layout_height="200dp" />
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
index 033fb0e..e4ca788 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
@@ -18,11 +18,12 @@
import android.app.Activity;
import android.content.Context;
-import android.graphics.BlurShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
import android.graphics.Shader;
import android.os.Bundle;
import android.view.Gravity;
@@ -51,16 +52,27 @@
}
public static class BlurGradientView extends View {
- private BlurShader mBlurShader = null;
- private Paint mPaint;
+ private final float mBlurRadius = 25f;
+ private final Paint mPaint;
+ private final RenderNode mRenderNode;
public BlurGradientView(Context c) {
super(c);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRenderNode = new RenderNode("BlurGradientView");
+ mRenderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(
+ mBlurRadius,
+ mBlurRadius,
+ null,
+ Shader.TileMode.DECAL
+ )
+ );
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (changed || mBlurShader == null) {
+ if (changed) {
LinearGradient gradient = new LinearGradient(
0f,
0f,
@@ -70,41 +82,81 @@
Color.YELLOW,
Shader.TileMode.CLAMP
);
- mBlurShader = new BlurShader(30f, 40f, gradient);
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setShader(mBlurShader);
+
+ mPaint.setShader(gradient);
+
+ final int width = right - left;
+ final int height = bottom - top;
+ mRenderNode.setPosition(0, 0, width, height);
+
+ Canvas canvas = mRenderNode.beginRecording();
+ canvas.drawRect(
+ mBlurRadius * 2,
+ mBlurRadius * 2,
+ width - mBlurRadius * 2,
+ height - mBlurRadius * 2,
+ mPaint
+ );
+ mRenderNode.endRecording();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+ canvas.drawRenderNode(mRenderNode);
}
}
public static class BlurView extends View {
- private final BlurShader mBlurShader;
private final Paint mPaint;
+ private final RenderNode mRenderNode;
+ private final float mBlurRadius = 20f;
public BlurView(Context c) {
super(c);
- mBlurShader = new BlurShader(20f, 20f, null);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setShader(mBlurShader);
+ mRenderNode = new RenderNode("blurNode");
+ mRenderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(
+ mBlurRadius,
+ mBlurRadius,
+ null,
+ Shader.TileMode.DECAL
+ )
+ );
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed) {
+ int width = right - left;
+ int height = bottom - top;
+ mRenderNode.setPosition(0, 0, width, height);
+ Canvas canvas = mRenderNode.beginRecording(width, height);
+ mPaint.setColor(Color.BLUE);
+
+ canvas.drawRect(
+ mBlurRadius * 2,
+ mBlurRadius * 2,
+ width - mBlurRadius * 2,
+ height - mBlurRadius * 2,
+ mPaint
+ );
+
+ mPaint.setColor(Color.RED);
+ canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint);
+
+ mRenderNode.endRecording();
+ }
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
- mPaint.setColor(Color.BLUE);
- canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
-
- mPaint.setColor(Color.RED);
- canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, 50f, mPaint);
+ canvas.drawRenderNode(mRenderNode);
}
}
}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
index 71175847..2f2578b 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -20,13 +20,13 @@
import android.graphics.BitmapFactory
import android.graphics.BitmapShader
import android.graphics.BlendMode
-import android.graphics.BlurShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Outline
import android.graphics.Paint
-import android.graphics.RadialGradient
import android.graphics.Rect
+import android.graphics.RenderEffect
+import android.graphics.RenderNode
import android.graphics.Shader
import android.hardware.Sensor
import android.hardware.SensorEvent
@@ -36,7 +36,6 @@
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.FrameLayout
-import com.android.internal.graphics.ColorUtils
import com.android.test.silkfx.R
import kotlin.math.sin
import kotlin.math.sqrt
@@ -152,10 +151,19 @@
var blurRadius = 150f
set(value) {
field = value
- blurPaint.shader = BlurShader(value, value, null)
+ renderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(value, value, Shader.TileMode.CLAMP))
invalidate()
}
+ private var renderNodeIsDirty = true
+ private val renderNode = RenderNode("GlassRenderNode")
+
+ override fun invalidate() {
+ renderNodeIsDirty = true
+ super.invalidate()
+ }
+
init {
setWillNotDraw(false)
materialPaint.blendMode = BlendMode.SOFT_LIGHT
@@ -164,7 +172,6 @@
scrimPaint.alpha = (scrimOpacity * 255).toInt()
noisePaint.alpha = (noiseOpacity * 255).toInt()
materialPaint.alpha = (materialOpacity * 255).toInt()
- blurPaint.shader = BlurShader(blurRadius, blurRadius, null)
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View?, outline: Outline?) {
outline?.setRoundRect(Rect(0, 0, width, height), 100f)
@@ -184,20 +191,8 @@
}
override fun onDraw(canvas: Canvas?) {
- src.set(-width / 2, -height / 2, width / 2, height / 2)
- src.scale(1.0f + zoom)
- val centerX = left + width / 2
- val centerY = top + height / 2
- val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
- val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
- src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
- src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)
-
- dst.set(0, 0, width, height)
- canvas?.drawBitmap(backgroundBitmap, src, dst, blurPaint)
- canvas?.drawRect(dst, materialPaint)
- canvas?.drawRect(dst, noisePaint)
- canvas?.drawRect(dst, scrimPaint)
+ updateGlassRenderNode()
+ canvas?.drawRenderNode(renderNode)
}
fun resetGyroOffsets() {
@@ -205,4 +200,31 @@
gyroYRotation = 0f
invalidate()
}
+
+ private fun updateGlassRenderNode() {
+ if (renderNodeIsDirty) {
+ renderNode.setPosition(0, 0, getWidth(), getHeight())
+
+ val canvas = renderNode.beginRecording()
+
+ src.set(-width / 2, -height / 2, width / 2, height / 2)
+ src.scale(1.0f + zoom)
+ val centerX = left + width / 2
+ val centerY = top + height / 2
+ val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
+ val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
+ src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
+ src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)
+
+ dst.set(0, 0, width, height)
+ canvas.drawBitmap(backgroundBitmap, src, dst, blurPaint)
+ canvas.drawRect(dst, materialPaint)
+ canvas.drawRect(dst, noisePaint)
+ canvas.drawRect(dst, scrimPaint)
+
+ renderNode.endRecording()
+
+ renderNodeIsDirty = false
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index 3c3076f..030ddd2 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -32,7 +32,6 @@
import static org.junit.Assert.fail;
import android.net.LinkProperties.ProvisioningChange;
-import android.net.util.LinkPropertiesUtils.CompareResult;
import android.os.Build;
import android.system.OsConstants;
import android.util.ArraySet;
@@ -41,6 +40,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 91c9a2a..6de31f6 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -22,11 +22,11 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.net.util.MacAddressUtils;
-
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.MacAddressUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/tools/xmlpersistence/Android.bp b/tools/xmlpersistence/Android.bp
new file mode 100644
index 0000000..d58d0dc
--- /dev/null
+++ b/tools/xmlpersistence/Android.bp
@@ -0,0 +1,11 @@
+java_binary_host {
+ name: "xmlpersistence_cli",
+ manifest: "manifest.txt",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "javaparser-symbol-solver",
+ "javapoet",
+ ],
+}
diff --git a/tools/xmlpersistence/OWNERS b/tools/xmlpersistence/OWNERS
new file mode 100644
index 0000000..4f4d06a
--- /dev/null
+++ b/tools/xmlpersistence/OWNERS
@@ -0,0 +1 @@
+zhanghai@google.com
diff --git a/tools/xmlpersistence/manifest.txt b/tools/xmlpersistence/manifest.txt
new file mode 100644
index 0000000..6d97719
--- /dev/null
+++ b/tools/xmlpersistence/manifest.txt
@@ -0,0 +1 @@
+Main-class: MainKt
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
new file mode 100644
index 0000000..28467b7
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -0,0 +1,578 @@
+/*
+ * 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.
+ */
+
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.NameAllocator
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeSpec
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.nio.charset.StandardCharsets
+import java.time.Year
+import java.util.Objects
+import javax.lang.model.element.Modifier
+
+// JavaPoet only supports line comments, and can't add a newline after file level comments.
+val FILE_HEADER = """
+ /*
+ * Copyright (C) ${Year.now().value} The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // Generated by xmlpersistence. DO NOT MODIFY!
+ // CHECKSTYLE:OFF
+ // @formatter:off
+""".trimIndent() + "\n\n"
+
+private val atomicFileType = ClassName.get("android.util", "AtomicFile")
+
+fun generate(persistence: PersistenceInfo): JavaFile {
+ val distinctClassFields = persistence.root.allClassFields.distinctBy { it.type }
+ val type = TypeSpec.classBuilder(persistence.name)
+ .addJavadoc(
+ """
+ Generated class implementing XML persistence for${'$'}W{@link $1T}.
+ <p>
+ This class provides atomicity for persistence via {@link $2T}, however it does not provide
+ thread safety, so please bring your own synchronization mechanism.
+ """.trimIndent(), persistence.root.type, atomicFileType
+ )
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addField(generateFileField())
+ .addMethod(generateConstructor())
+ .addMethod(generateReadMethod(persistence.root))
+ .addMethod(generateParseMethod(persistence.root))
+ .addMethods(distinctClassFields.map { generateParseClassMethod(it) })
+ .addMethod(generateWriteMethod(persistence.root))
+ .addMethod(generateSerializeMethod(persistence.root))
+ .addMethods(distinctClassFields.map { generateSerializeClassMethod(it) })
+ .addMethod(generateDeleteMethod())
+ .build()
+ return JavaFile.builder(persistence.root.type.packageName(), type)
+ .skipJavaLangImports(true)
+ .indent(" ")
+ .build()
+}
+
+private val nonNullType = ClassName.get("android.annotation", "NonNull")
+
+private fun generateFileField(): FieldSpec =
+ FieldSpec.builder(atomicFileType, "mFile", Modifier.PRIVATE, Modifier.FINAL)
+ .addAnnotation(nonNullType)
+ .build()
+
+private fun generateConstructor(): MethodSpec =
+ MethodSpec.constructorBuilder()
+ .addJavadoc(
+ """
+ Create an instance of this class.
+
+ @param file the XML file for persistence
+ """.trimIndent()
+ )
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(
+ ParameterSpec.builder(File::class.java, "file").addAnnotation(nonNullType).build()
+ )
+ .addStatement("mFile = new \$1T(file)", atomicFileType)
+ .build()
+
+private val nullableType = ClassName.get("android.annotation", "Nullable")
+
+private val xmlPullParserType = ClassName.get("org.xmlpull.v1", "XmlPullParser")
+
+private val xmlType = ClassName.get("android.util", "Xml")
+
+private val xmlPullParserExceptionType = ClassName.get("org.xmlpull.v1", "XmlPullParserException")
+
+private fun generateReadMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("read")
+ .addJavadoc(
+ """
+ Read${'$'}W{@link $1T}${'$'}Wfrom${'$'}Wthe${'$'}WXML${'$'}Wfile.
+
+ @return the persisted${'$'}W{@link $1T},${'$'}Wor${'$'}W{@code null}${'$'}Wif${'$'}Wthe${'$'}WXML${'$'}Wfile${'$'}Wdoesn't${'$'}Wexist
+ @throws IllegalArgumentException if an error occurred while reading
+ """.trimIndent(), rootField.type
+ )
+ .addAnnotation(nullableType)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(rootField.type)
+ .addControlFlow(
+ "try (final \$1T inputStream = mFile.openRead())", FileInputStream::class.java
+ ) {
+ addStatement("final \$1T parser = \$2T.newPullParser()", xmlPullParserType, xmlType)
+ addStatement("parser.setInput(inputStream, null)")
+ addStatement("return parse(parser)")
+ nextControlFlow("catch (\$1T e)", FileNotFoundException::class.java)
+ addStatement("return null")
+ nextControlFlow(
+ "catch (\$1T | \$2T e)", IOException::class.java, xmlPullParserExceptionType
+ )
+ addStatement("throw new IllegalArgumentException(e)")
+ }
+ .build()
+
+private val ClassFieldInfo.allClassFields: List<ClassFieldInfo>
+ get() =
+ mutableListOf<ClassFieldInfo>().apply {
+ this += this@allClassFields
+ for (field in fields) {
+ when (field) {
+ is ClassFieldInfo -> this += field.allClassFields
+ is ListFieldInfo -> this += field.element.allClassFields
+ }
+ }
+ }
+
+private fun generateParseMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("parse")
+ .addAnnotation(nonNullType)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(rootField.type)
+ .addParameter(
+ ParameterSpec.builder(xmlPullParserType, "parser").addAnnotation(nonNullType).build()
+ )
+ .addExceptions(listOf(ClassName.get(IOException::class.java), xmlPullParserExceptionType))
+ .apply {
+ addStatement("int type")
+ addStatement("int depth")
+ addStatement("int innerDepth = parser.getDepth() + 1")
+ addControlFlow(
+ "while ((type = parser.next()) != \$1T.END_DOCUMENT\$W"
+ + "&& ((depth = parser.getDepth()) >= innerDepth || type != \$1T.END_TAG))",
+ xmlPullParserType
+ ) {
+ addControlFlow(
+ "if (depth > innerDepth || type != \$1T.START_TAG)", xmlPullParserType
+ ) {
+ addStatement("continue")
+ }
+ addControlFlow(
+ "if (\$1T.equals(parser.getName(),\$W\$2S))", Objects::class.java,
+ rootField.tagName
+ ) {
+ addStatement("return \$1L(parser)", rootField.parseMethodName)
+ }
+ }
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Missing root tag <${rootField.tagName}>"
+ )
+ }
+ .build()
+
+private fun generateParseClassMethod(classField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder(classField.parseMethodName)
+ .addAnnotation(nonNullType)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(classField.type)
+ .addParameter(
+ ParameterSpec.builder(xmlPullParserType, "parser").addAnnotation(nonNullType).build()
+ )
+ .apply {
+ val (attributeFields, tagFields) = classField.fields
+ .partition { it is PrimitiveFieldInfo || it is StringFieldInfo }
+ if (tagFields.isNotEmpty()) {
+ addExceptions(
+ listOf(ClassName.get(IOException::class.java), xmlPullParserExceptionType)
+ )
+ }
+ val nameAllocator = NameAllocator().apply {
+ newName("parser")
+ newName("type")
+ newName("depth")
+ newName("innerDepth")
+ }
+ for (field in attributeFields) {
+ val variableName = nameAllocator.newName(field.variableName, field)
+ when (field) {
+ is PrimitiveFieldInfo -> {
+ val stringVariableName =
+ nameAllocator.newName("${field.variableName}String")
+ addStatement(
+ "final String \$1L =\$Wparser.getAttributeValue(null,\$W\$2S)",
+ stringVariableName, field.attributeName
+ )
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", stringVariableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Missing attribute \"${field.attributeName}\""
+ )
+ }
+ }
+ val boxedType = field.type.box()
+ val parseTypeMethodName = if (field.type.isPrimitive) {
+ "parse${field.type.toString().capitalize()}"
+ } else {
+ "valueOf"
+ }
+ if (field.isRequired) {
+ addStatement(
+ "final \$1T \$2L =\$W\$3T.\$4L($5L)", field.type, variableName,
+ boxedType, parseTypeMethodName, stringVariableName
+ )
+ } else {
+ addStatement(
+ "final \$1T \$2L =\$W$3L != null ?\$W\$4T.\$5L($3L)\$W: null",
+ field.type, variableName, stringVariableName, boxedType,
+ parseTypeMethodName
+ )
+ }
+ }
+ is StringFieldInfo ->
+ addStatement(
+ "final String \$1L =\$Wparser.getAttributeValue(null,\$W\$2S)",
+ variableName, field.attributeName
+ )
+ else -> error(field)
+ }
+ }
+ if (tagFields.isNotEmpty()) {
+ for (field in tagFields) {
+ val variableName = nameAllocator.newName(field.variableName, field)
+ when (field) {
+ is ClassFieldInfo ->
+ addStatement("\$1T \$2L =\$Wnull", field.type, variableName)
+ is ListFieldInfo ->
+ addStatement(
+ "final \$1T \$2L =\$Wnew \$3T<>()", field.type, variableName,
+ ArrayList::class.java
+ )
+ else -> error(field)
+ }
+ }
+ addStatement("int type")
+ addStatement("int depth")
+ addStatement("int innerDepth = parser.getDepth() + 1")
+ addControlFlow(
+ "while ((type = parser.next()) != \$1T.END_DOCUMENT\$W"
+ + "&& ((depth = parser.getDepth()) >= innerDepth || type != \$1T.END_TAG))",
+ xmlPullParserType
+ ) {
+ addControlFlow(
+ "if (depth > innerDepth || type != \$1T.START_TAG)", xmlPullParserType
+ ) {
+ addStatement("continue")
+ }
+ addControlFlow("switch (parser.getName())") {
+ for (field in tagFields) {
+ addControlFlow("case \$1S:", field.tagName) {
+ val variableName = nameAllocator.get(field)
+ when (field) {
+ is ClassFieldInfo -> {
+ addControlFlow("if (\$1L != null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Duplicate tag \"${field.tagName}\""
+ )
+ }
+ addStatement(
+ "\$1L =\$W\$2L(parser)", variableName,
+ field.parseMethodName
+ )
+ addStatement("break")
+ }
+ is ListFieldInfo -> {
+ val elementNameAllocator = nameAllocator.clone()
+ val elementVariableName = elementNameAllocator.newName(
+ field.element.xmlName!!.toLowerCamelCase()
+ )
+ addStatement(
+ "final \$1T \$2L =\$W\$3L(parser)", field.element.type,
+ elementVariableName, field.element.parseMethodName
+ )
+ addStatement(
+ "\$1L.add(\$2L)", variableName, elementVariableName
+ )
+ addStatement("break")
+ }
+ else -> error(field)
+ }
+ }
+ }
+ }
+ }
+ }
+ for (field in tagFields.filter { it is ClassFieldInfo && it.isRequired }) {
+ addControlFlow("if ($1L == null)", nameAllocator.get(field)) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)", "Missing tag <${field.tagName}>"
+ )
+ }
+ }
+ addStatement(
+ classField.fields.joinToString(",\$W", "return new \$1T(", ")") {
+ nameAllocator.get(it)
+ }, classField.type
+ )
+ }
+ .build()
+
+private val ClassFieldInfo.parseMethodName: String
+ get() = "parse${type.simpleName().toUpperCamelCase()}"
+
+private val xmlSerializerType = ClassName.get("org.xmlpull.v1", "XmlSerializer")
+
+private fun generateWriteMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("write")
+ .apply {
+ val nameAllocator = NameAllocator().apply {
+ newName("outputStream")
+ newName("serializer")
+ }
+ val parameterName = nameAllocator.newName(rootField.variableName)
+ addJavadoc(
+ """
+ Write${'$'}W{@link $1T}${'$'}Wto${'$'}Wthe${'$'}WXML${'$'}Wfile.
+
+ @param $2L the${'$'}W{@link ${'$'}1T}${'$'}Wto${'$'}Wpersist
+ """.trimIndent(), rootField.type, parameterName
+ )
+ addAnnotation(nullableType)
+ addModifiers(Modifier.PUBLIC)
+ addParameter(
+ ParameterSpec.builder(rootField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addStatement("\$1T outputStream = null", FileOutputStream::class.java)
+ addControlFlow("try") {
+ addStatement("outputStream = mFile.startWrite()")
+ addStatement(
+ "final \$1T serializer =\$W\$2T.newSerializer()", xmlSerializerType, xmlType
+ )
+ addStatement(
+ "serializer.setOutput(outputStream, \$1T.UTF_8.name())",
+ StandardCharsets::class.java
+ )
+ addStatement(
+ "serializer.setFeature(\$1S, true)",
+ "http://xmlpull.org/v1/doc/features.html#indent-output"
+ )
+ addStatement("serializer.startDocument(null, true)")
+ addStatement("serialize(serializer,\$W\$1L)", parameterName)
+ addStatement("serializer.endDocument()")
+ addStatement("mFile.finishWrite(outputStream)")
+ nextControlFlow("catch (Exception e)")
+ addStatement("e.printStackTrace()")
+ addStatement("mFile.failWrite(outputStream)")
+ }
+ }
+ .build()
+
+private fun generateSerializeMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("serialize")
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(xmlSerializerType, "serializer")
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ .apply {
+ val nameAllocator = NameAllocator().apply { newName("serializer") }
+ val parameterName = nameAllocator.newName(rootField.variableName)
+ addParameter(
+ ParameterSpec.builder(rootField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addException(IOException::class.java)
+ addStatement("serializer.startTag(null, \$1S)", rootField.tagName)
+ addStatement("\$1L(serializer, \$2L)", rootField.serializeMethodName, parameterName)
+ addStatement("serializer.endTag(null, \$1S)", rootField.tagName)
+ }
+ .build()
+
+private fun generateSerializeClassMethod(classField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder(classField.serializeMethodName)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(xmlSerializerType, "serializer")
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ .apply {
+ val nameAllocator = NameAllocator().apply {
+ newName("serializer")
+ newName("i")
+ }
+ val parameterName = nameAllocator.newName(classField.serializeParameterName)
+ addParameter(
+ ParameterSpec.builder(classField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addException(IOException::class.java)
+ val (attributeFields, tagFields) = classField.fields
+ .partition { it is PrimitiveFieldInfo || it is StringFieldInfo }
+ for (field in attributeFields) {
+ val variableName = "$parameterName.${field.name}"
+ if (!field.isRequired) {
+ beginControlFlow("if (\$1L != null)", variableName)
+ }
+ when (field) {
+ is PrimitiveFieldInfo -> {
+ if (field.isRequired && !field.type.isPrimitive) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ val stringVariableName =
+ nameAllocator.newName("${field.variableName}String")
+ addStatement(
+ "final String \$1L =\$WString.valueOf(\$2L)", stringVariableName,
+ variableName
+ )
+ addStatement(
+ "serializer.attribute(null, \$1S, \$2L)", field.attributeName,
+ stringVariableName
+ )
+ }
+ is StringFieldInfo -> {
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ addStatement(
+ "serializer.attribute(null, \$1S, \$2L)", field.attributeName,
+ variableName
+ )
+ }
+ else -> error(field)
+ }
+ if (!field.isRequired) {
+ endControlFlow()
+ }
+ }
+ for (field in tagFields) {
+ val variableName = "$parameterName.${field.name}"
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ when (field) {
+ is ClassFieldInfo -> {
+ addStatement("serializer.startTag(null, \$1S)", field.tagName)
+ addStatement(
+ "\$1L(serializer, \$2L)", field.serializeMethodName, variableName
+ )
+ addStatement("serializer.endTag(null, \$1S)", field.tagName)
+ }
+ is ListFieldInfo -> {
+ val sizeVariableName = nameAllocator.newName("${field.variableName}Size")
+ addStatement(
+ "final int \$1L =\$W\$2L.size()", sizeVariableName, variableName
+ )
+ addControlFlow("for (int i = 0;\$Wi < \$1L;\$Wi++)", sizeVariableName) {
+ val elementNameAllocator = nameAllocator.clone()
+ val elementVariableName = elementNameAllocator.newName(
+ field.element.xmlName!!.toLowerCamelCase()
+ )
+ addStatement(
+ "final \$1T \$2L =\$W\$3L.get(i)", field.element.type,
+ elementVariableName, variableName
+ )
+ addControlFlow("if (\$1L == null)", elementVariableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S\$W+ i\$W+ \$2S)",
+ "Field element \"${field.name}[", "]\" is null"
+ )
+ }
+ addStatement("serializer.startTag(null, \$1S)", field.element.tagName)
+ addStatement(
+ "\$1L(serializer,\$W\$2L)", field.element.serializeMethodName,
+ elementVariableName
+ )
+ addStatement("serializer.endTag(null, \$1S)", field.element.tagName)
+ }
+ }
+ else -> error(field)
+ }
+ }
+ }
+ .build()
+
+private val ClassFieldInfo.serializeMethodName: String
+ get() = "serialize${type.simpleName().toUpperCamelCase()}"
+
+private val ClassFieldInfo.serializeParameterName: String
+ get() = type.simpleName().toLowerCamelCase()
+
+private val FieldInfo.variableName: String
+ get() = name.toLowerCamelCase()
+
+private val FieldInfo.attributeName: String
+ get() {
+ check(this is PrimitiveFieldInfo || this is StringFieldInfo)
+ return xmlNameOrName.toLowerCamelCase()
+ }
+
+private val FieldInfo.tagName: String
+ get() {
+ check(this is ClassFieldInfo || this is ListFieldInfo)
+ return xmlNameOrName.toLowerKebabCase()
+ }
+
+private val FieldInfo.xmlNameOrName: String
+ get() = xmlName ?: name
+
+private fun generateDeleteMethod(): MethodSpec =
+ MethodSpec.methodBuilder("delete")
+ .addJavadoc("Delete the XML file, if any.")
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("mFile.delete()")
+ .build()
+
+private inline fun MethodSpec.Builder.addControlFlow(
+ controlFlow: String,
+ vararg args: Any,
+ block: MethodSpec.Builder.() -> Unit
+): MethodSpec.Builder {
+ beginControlFlow(controlFlow, *args)
+ block()
+ endControlFlow()
+ return this
+}
diff --git a/tools/xmlpersistence/src/main/kotlin/Main.kt b/tools/xmlpersistence/src/main/kotlin/Main.kt
new file mode 100644
index 0000000..e271f8c
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Main.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File
+import java.nio.file.Files
+
+fun main(args: Array<String>) {
+ val showUsage = args.isEmpty() || when (args.singleOrNull()) {
+ "-h", "--help" -> true
+ else -> false
+ }
+ if (showUsage) {
+ usage()
+ return
+ }
+
+ val files = args.flatMap {
+ File(it).walk().filter { it.isFile && it.extension == "java" }.map { it.toPath() }
+ }
+ val persistences = parse(files)
+ for (persistence in persistences) {
+ val file = generate(persistence)
+ Files.newBufferedWriter(persistence.path).use {
+ it.write(FILE_HEADER)
+ file.writeTo(it)
+ }
+ }
+}
+
+private fun usage() {
+ println("Usage: xmlpersistence <FILES>")
+}
diff --git a/tools/xmlpersistence/src/main/kotlin/Parser.kt b/tools/xmlpersistence/src/main/kotlin/Parser.kt
new file mode 100644
index 0000000..3ea12a9
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Parser.kt
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+
+import com.github.javaparser.JavaParser
+import com.github.javaparser.ParseProblemException
+import com.github.javaparser.ParseResult
+import com.github.javaparser.ParserConfiguration
+import com.github.javaparser.ast.Node
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
+import com.github.javaparser.ast.body.FieldDeclaration
+import com.github.javaparser.ast.body.TypeDeclaration
+import com.github.javaparser.ast.expr.AnnotationExpr
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.NormalAnnotationExpr
+import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.StringLiteralExpr
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType
+import com.github.javaparser.resolution.types.ResolvedReferenceType
+import com.github.javaparser.symbolsolver.JavaSymbolSolver
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration
+import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver
+import com.github.javaparser.symbolsolver.resolution.typesolvers.MemoryTypeSolver
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import java.nio.file.Path
+import java.util.Optional
+
+class PersistenceInfo(
+ val name: String,
+ val root: ClassFieldInfo,
+ val path: Path
+)
+
+sealed class FieldInfo {
+ abstract val name: String
+ abstract val xmlName: String?
+ abstract val type: TypeName
+ abstract val isRequired: Boolean
+}
+
+class PrimitiveFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: TypeName,
+ override val isRequired: Boolean
+) : FieldInfo()
+
+class StringFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val isRequired: Boolean
+) : FieldInfo() {
+ override val type: TypeName = ClassName.get(String::class.java)
+}
+
+class ClassFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: ClassName,
+ override val isRequired: Boolean,
+ val fields: List<FieldInfo>
+) : FieldInfo()
+
+class ListFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: ParameterizedTypeName,
+ val element: ClassFieldInfo
+) : FieldInfo() {
+ override val isRequired: Boolean = true
+}
+
+fun parse(files: List<Path>): List<PersistenceInfo> {
+ val typeSolver = CombinedTypeSolver().apply { add(ReflectionTypeSolver()) }
+ val javaParser = JavaParser(ParserConfiguration()
+ .setSymbolResolver(JavaSymbolSolver(typeSolver)))
+ val compilationUnits = files.map { javaParser.parse(it).getOrThrow() }
+ val memoryTypeSolver = MemoryTypeSolver().apply {
+ for (compilationUnit in compilationUnits) {
+ for (typeDeclaration in compilationUnit.getNodesByClass<TypeDeclaration<*>>()) {
+ val name = typeDeclaration.fullyQualifiedName.getOrNull() ?: continue
+ addDeclaration(name, typeDeclaration.resolve())
+ }
+ }
+ }
+ typeSolver.add(memoryTypeSolver)
+ return mutableListOf<PersistenceInfo>().apply {
+ for (compilationUnit in compilationUnits) {
+ val classDeclarations = compilationUnit
+ .getNodesByClass<ClassOrInterfaceDeclaration>()
+ .filter { !it.isInterface && (!it.isNestedType || it.isStatic) }
+ this += classDeclarations.mapNotNull { parsePersistenceInfo(it) }
+ }
+ }
+}
+
+private fun parsePersistenceInfo(classDeclaration: ClassOrInterfaceDeclaration): PersistenceInfo? {
+ val annotation = classDeclaration.getAnnotationByName("XmlPersistence").getOrNull()
+ ?: return null
+ val rootClassName = classDeclaration.nameAsString
+ val name = annotation.getMemberValue("value")?.stringLiteralValue
+ ?: "${rootClassName}Persistence"
+ val rootXmlName = classDeclaration.getAnnotationByName("XmlName").getOrNull()
+ ?.getMemberValue("value")?.stringLiteralValue
+ val root = parseClassFieldInfo(
+ rootXmlName ?: rootClassName, rootXmlName, true, classDeclaration
+ )
+ val path = classDeclaration.findCompilationUnit().get().storage.get().path
+ .resolveSibling("$name.java")
+ return PersistenceInfo(name, root, path)
+}
+
+private fun parseClassFieldInfo(
+ name: String,
+ xmlName: String?,
+ isRequired: Boolean,
+ classDeclaration: ClassOrInterfaceDeclaration
+): ClassFieldInfo {
+ val fields = classDeclaration.fields.filterNot { it.isStatic }.map { parseFieldInfo(it) }
+ val type = classDeclaration.resolve().typeName
+ return ClassFieldInfo(name, xmlName, type, isRequired, fields)
+}
+
+private fun parseFieldInfo(field: FieldDeclaration): FieldInfo {
+ require(field.isPublic && field.isFinal)
+ val variable = field.variables.single()
+ val name = variable.nameAsString
+ val annotations = field.annotations + variable.type.annotations
+ val annotation = annotations.getByName("XmlName")
+ val xmlName = annotation?.getMemberValue("value")?.stringLiteralValue
+ val isRequired = annotations.getByName("NonNull") != null
+ return when (val type = variable.type.resolve()) {
+ is ResolvedPrimitiveType -> {
+ val primitiveType = type.typeName
+ PrimitiveFieldInfo(name, xmlName, primitiveType, true)
+ }
+ is ResolvedReferenceType -> {
+ when (type.qualifiedName) {
+ Boolean::class.javaObjectType.name, Byte::class.javaObjectType.name,
+ Short::class.javaObjectType.name, Char::class.javaObjectType.name,
+ Integer::class.javaObjectType.name, Long::class.javaObjectType.name,
+ Float::class.javaObjectType.name, Double::class.javaObjectType.name ->
+ PrimitiveFieldInfo(name, xmlName, type.typeName, isRequired)
+ String::class.java.name -> StringFieldInfo(name, xmlName, isRequired)
+ List::class.java.name -> {
+ requireNotNull(xmlName)
+ val elementType = type.typeParametersValues().single()
+ require(elementType is ResolvedReferenceType)
+ val listType = ParameterizedTypeName.get(
+ ClassName.get(List::class.java), elementType.typeName
+ )
+ val element = parseClassFieldInfo(
+ "(element)", xmlName, true, elementType.classDeclaration
+ )
+ ListFieldInfo(name, xmlName, listType, element)
+ }
+ else -> parseClassFieldInfo(name, xmlName, isRequired, type.classDeclaration)
+ }
+ }
+ else -> error(type)
+ }
+}
+
+private fun <T> ParseResult<T>.getOrThrow(): T =
+ if (isSuccessful) {
+ result.get()
+ } else {
+ throw ParseProblemException(problems)
+ }
+
+private inline fun <reified T : Node> Node.getNodesByClass(): List<T> =
+ getNodesByClass(T::class.java)
+
+private fun <T : Node> Node.getNodesByClass(klass: Class<T>): List<T> = mutableListOf<T>().apply {
+ if (klass.isInstance(this@getNodesByClass)) {
+ this += klass.cast(this@getNodesByClass)
+ }
+ for (childNode in childNodes) {
+ this += childNode.getNodesByClass(klass)
+ }
+}
+
+private fun <T> Optional<T>.getOrNull(): T? = orElse(null)
+
+private fun List<AnnotationExpr>.getByName(name: String): AnnotationExpr? =
+ find { it.name.identifier == name }
+
+private fun AnnotationExpr.getMemberValue(name: String): Expression? =
+ when (this) {
+ is NormalAnnotationExpr -> pairs.find { it.nameAsString == name }?.value
+ is SingleMemberAnnotationExpr -> if (name == "value") memberValue else null
+ else -> null
+ }
+
+private val Expression.stringLiteralValue: String
+ get() {
+ require(this is StringLiteralExpr)
+ return value
+ }
+
+private val ResolvedReferenceType.classDeclaration: ClassOrInterfaceDeclaration
+ get() {
+ val resolvedClassDeclaration = typeDeclaration
+ require(resolvedClassDeclaration is JavaParserClassDeclaration)
+ return resolvedClassDeclaration.wrappedNode
+ }
+
+private val ResolvedPrimitiveType.typeName: TypeName
+ get() =
+ when (this) {
+ ResolvedPrimitiveType.BOOLEAN -> TypeName.BOOLEAN
+ ResolvedPrimitiveType.BYTE -> TypeName.BYTE
+ ResolvedPrimitiveType.SHORT -> TypeName.SHORT
+ ResolvedPrimitiveType.CHAR -> TypeName.CHAR
+ ResolvedPrimitiveType.INT -> TypeName.INT
+ ResolvedPrimitiveType.LONG -> TypeName.LONG
+ ResolvedPrimitiveType.FLOAT -> TypeName.FLOAT
+ ResolvedPrimitiveType.DOUBLE -> TypeName.DOUBLE
+ }
+
+// This doesn't support type parameters.
+private val ResolvedReferenceType.typeName: TypeName
+ get() = typeDeclaration.typeName
+
+private val ResolvedReferenceTypeDeclaration.typeName: ClassName
+ get() {
+ val packageName = packageName
+ val classNames = className.split(".")
+ val topLevelClassName = classNames.first()
+ val nestedClassNames = classNames.drop(1)
+ return ClassName.get(packageName, topLevelClassName, *nestedClassNames.toTypedArray())
+ }
diff --git a/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt b/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt
new file mode 100644
index 0000000..b4bdbba
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt
@@ -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.
+ */
+
+import java.util.Locale
+
+private val camelHumpBoundary = Regex(
+ "-"
+ + "|_"
+ + "|(?<=[0-9])(?=[^0-9])"
+ + "|(?<=[A-Z])(?=[^A-Za-z]|[A-Z][a-z])"
+ + "|(?<=[a-z])(?=[^a-z])"
+)
+
+private fun String.toCamelHumps(): List<String> = split(camelHumpBoundary)
+
+fun String.toUpperCamelCase(): String =
+ toCamelHumps().joinToString("") { it.toLowerCase(Locale.ROOT).capitalize(Locale.ROOT) }
+
+fun String.toLowerCamelCase(): String = toUpperCamelCase().decapitalize(Locale.ROOT)
+
+fun String.toUpperKebabCase(): String =
+ toCamelHumps().joinToString("-") { it.toUpperCase(Locale.ROOT) }
+
+fun String.toLowerKebabCase(): String =
+ toCamelHumps().joinToString("-") { it.toLowerCase(Locale.ROOT) }
+
+fun String.toUpperSnakeCase(): String =
+ toCamelHumps().joinToString("_") { it.toUpperCase(Locale.ROOT) }
+
+fun String.toLowerSnakeCase(): String =
+ toCamelHumps().joinToString("_") { it.toLowerCase(Locale.ROOT) }
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index fd45ebe..0f71ab4 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -509,7 +509,7 @@
field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
field public static final String EXTRA_CHANGE_REASON = "changeReason";
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 @Deprecated 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";
field public static final String EXTRA_URL = "android.net.wifi.extra.URL";
@@ -517,7 +517,7 @@
field public static final String EXTRA_WIFI_AP_INTERFACE_NAME = "android.net.wifi.extra.WIFI_AP_INTERFACE_NAME";
field public static final String EXTRA_WIFI_AP_MODE = "android.net.wifi.extra.WIFI_AP_MODE";
field public static final String EXTRA_WIFI_AP_STATE = "wifi_state";
- field public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
+ field @Deprecated public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
field public static final String EXTRA_WIFI_CREDENTIAL_EVENT_TYPE = "et";
field public static final String EXTRA_WIFI_CREDENTIAL_SSID = "ssid";
field public static final int IFACE_IP_MODE_CONFIGURATION_ERROR = 0; // 0x0
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index bc837b3..237922a 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -33,7 +33,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -767,10 +766,6 @@
}
} else {
Preconditions.checkStringNotEmpty(passphrase);
- final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
- if (!asciiEncoder.canEncode(passphrase)) {
- throw new IllegalArgumentException("passphrase not ASCII encodable");
- }
if (securityType == SECURITY_TYPE_WPA2_PSK
|| securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION) {
if (passphrase.length() < PSK_MIN_LEN || passphrase.length() > PSK_MAX_LEN) {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 391be35..0973d54 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -29,7 +29,6 @@
import android.net.ProxyInfo;
import android.net.StaticIpConfiguration;
import android.net.Uri;
-import android.net.util.MacAddressUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,6 +40,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.MacAddressUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 2219bfc..b4e4210 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -931,9 +931,6 @@
* This can be as a result of adding/updating/deleting a network.
* <br />
* {@link #EXTRA_CHANGE_REASON} contains whether the configuration was added/changed/removed.
- * {@link #EXTRA_WIFI_CONFIGURATION} is never set starting in Android 11.
- * {@link #EXTRA_MULTIPLE_NETWORKS_CHANGED} is set for backwards compatibility reasons, but
- * its value is always true, even if only a single network changed.
* <br />
* The {@link android.Manifest.permission#ACCESS_WIFI_STATE ACCESS_WIFI_STATE} permission is
* required to receive this broadcast.
@@ -947,17 +944,20 @@
* The lookup key for a {@link android.net.wifi.WifiConfiguration} object representing
* the changed Wi-Fi configuration when the {@link #CONFIGURED_NETWORKS_CHANGED_ACTION}
* broadcast is sent.
- * Note: this extra is never set starting in Android 11.
+ * @deprecated this extra is never set. Use {@link #getConfiguredNetworks} to get the full list
+ * of configured networks.
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_WIFI_CONFIGURATION = "wifiConfiguration";
/**
* Multiple network configurations have changed.
* @see #CONFIGURED_NETWORKS_CHANGED_ACTION
- * Note: this extra is always true starting in Android 11.
+ * @deprecated this extra's value is always true.
* @hide
*/
+ @Deprecated
@SystemApi
public static final String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges";
/**
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
index dad431c1..e2f40cf 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
@@ -17,10 +17,11 @@
package android.net.wifi.p2p.nsd;
import android.compat.annotation.UnsupportedAppUsage;
-import android.net.util.nsd.DnsSdTxtRecord;
import android.os.Build;
import android.text.TextUtils;
+import com.android.net.module.util.DnsSdTxtRecord;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
diff --git a/wifi/java/android/net/wifi/util/SdkLevelUtil.java b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
index 042634c7..d08d4fd 100644
--- a/wifi/java/android/net/wifi/util/SdkLevelUtil.java
+++ b/wifi/java/android/net/wifi/util/SdkLevelUtil.java
@@ -23,17 +23,17 @@
*
* This can be used to disable new Wifi APIs added in Mainline updates on older SDK versions.
*
+ * Note: if certain functionality is gated with SdkLevelUtil, its corresponding unit tests should
+ * also be gated by the same condition. Then, those unit tests will only be exercised on a base
+ * system image satisfying that condition.
+ * Alternatively, it can be tested via static mocking.
+ *
* @hide
*/
public class SdkLevelUtil {
- /** This class is instantiable to allow easy mocking. */
- public SdkLevelUtil() { }
-
- /** See {@link #isAtLeastS()}. This version is non-static to allow easy mocking. */
- public boolean isAtLeastSMockable() {
- return isAtLeastS();
- }
+ /** This class is not instantiable. */
+ private SdkLevelUtil() {}
/** Returns true if the Android platform SDK is at least "S", false otherwise. */
public static boolean isAtLeastS() {
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 62220a6..d4b2051 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -33,13 +33,14 @@
import static org.junit.Assert.assertTrue;
import android.net.MacAddress;
-import android.net.util.MacAddressUtils;
import android.net.wifi.WifiConfiguration.KeyMgmt;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
+import com.android.net.module.util.MacAddressUtils;
+
import org.junit.Before;
import org.junit.Test;