Merge "Add javadoc for package-name arg passed during the process start."
diff --git a/Android.bp b/Android.bp
index 970d66b..aeb3ceb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,8 +25,8 @@
//
// READ ME: ########################################################
-java_library {
- name: "framework",
+java_defaults {
+ name: "framework-defaults",
installable: true,
srcs: [
@@ -381,6 +381,7 @@
"core/java/com/android/internal/backup/IBackupTransport.aidl",
"core/java/com/android/internal/backup/IObbBackupService.aidl",
"core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl",
+ "core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl",
"core/java/com/android/internal/net/INetworkWatchlistManager.aidl",
"core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl",
"core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl",
@@ -554,6 +555,7 @@
"telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
"telephony/java/com/android/internal/telephony/ISms.aidl",
"telephony/java/com/android/internal/telephony/ISub.aidl",
+ "telephony/java/com/android/internal/telephony/IAnas.aidl",
"telephony/java/com/android/internal/telephony/ITelephony.aidl",
"telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
"telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
@@ -681,6 +683,7 @@
static_libs: [
"framework-protos",
+ "mediaplayer2-protos",
"android.hidl.base-V1.0-java",
"android.hardware.cas-V1.0-java",
"android.hardware.contexthub-V1.0-java",
@@ -703,12 +706,38 @@
"libmedia2_jni",
],
- javac_shard_size: 150,
-
dxflags: [
"--core-library",
"--multi-dex",
],
+
+}
+
+java_library {
+ name: "framework",
+ defaults: ["framework-defaults"],
+ javac_shard_size: 150,
+}
+
+java_library {
+ name: "framework-annotation-proc",
+ defaults: ["framework-defaults"],
+ // Use UsedByApps annotation processor
+ annotation_processors: ["unsupportedappusage-annotation-processor"],
+ // b/25860419: annotation processors must be explicitly specified for grok
+ annotation_processor_classes: [
+ "android.processor.unsupportedappusage.UsedByAppsProcessor",
+ ],
+}
+
+// A host library including just UnsupportedAppUsage.java so that the annotation
+// processor can also use this annotation.
+java_library_host {
+ name: "unsupportedappusage-annotation",
+ srcs: [
+ "core/java/android/annotation/IntDef.java",
+ "core/java/android/annotation/UnsupportedAppUsage.java",
+ ],
}
// A temporary build target that is conditionally included on the bootclasspath if
diff --git a/Android.mk b/Android.mk
index 988c009..5c4c237 100644
--- a/Android.mk
+++ b/Android.mk
@@ -322,6 +322,11 @@
( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
# ==== hiddenapi lists =======================================
+.KATI_RESTAT: \
+ $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
+ $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST): \
.KATI_IMPLICIT_OUTPUTS := \
$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
@@ -346,10 +351,14 @@
$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)) \
$(PRIVATE_GREYLIST_INPUTS) \
--input-blacklists frameworks/base/config/hiddenapi-force-blacklist.txt \
- --output-whitelist $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST) \
- --output-light-greylist $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
- --output-dark-greylist $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
- --output-blacklist $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+ --output-whitelist $(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST).tmp \
+ --output-light-greylist $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST).tmp \
+ --output-dark-greylist $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST).tmp \
+ --output-blacklist $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST).tmp
+ $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_WHITELIST))
+ $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST))
+ $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST))
+ $(call commit-change-for-toc,$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST))
# Include subdirectory makefiles
# ============================================================
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 66a2600d..d8d4a6e 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -23,8 +23,7 @@
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal.CallSession;
-
-import java.util.Random;
+import com.android.internal.os.CachedDeviceState;
import org.junit.After;
import org.junit.Before;
@@ -32,8 +31,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertNull;
-
/**
* Performance tests for {@link BinderCallsStats}
@@ -49,6 +46,8 @@
@Before
public void setUp() {
mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
+ CachedDeviceState deviceState = new CachedDeviceState(false, false);
+ mBinderCallsStats.setDeviceState(deviceState.getReadonlyClient());
}
@After
diff --git a/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
new file mode 100644
index 0000000..0f880b7
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 android.os;
+
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.os.LooperStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+/**
+ * Performance tests for {@link LooperStats}.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class LooperStatsPerfTest {
+ private static final int DISTINCT_MESSAGE_COUNT = 1000;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private LooperStats mStats;
+ private CachedDeviceState mDeviceState;
+ private HandlerThread mThread;
+ private Message[] mMessages = new Message[DISTINCT_MESSAGE_COUNT];
+
+ @Before
+ public void setUp() {
+ mStats = new LooperStats(1, DISTINCT_MESSAGE_COUNT - 1);
+ mDeviceState = new CachedDeviceState(false, false);
+ mStats.setDeviceState(mDeviceState.getReadonlyClient());
+ // The tests are all single-threaded. HandlerThread is created to allow creating Handlers.
+ mThread = new HandlerThread("UnusedThread");
+ mThread.start();
+ for (int i = 0; i < DISTINCT_MESSAGE_COUNT; i++) {
+ mMessages[i] = mThread.getThreadHandler().obtainMessage(i);
+ }
+ }
+
+ @After
+ public void tearDown() {
+ mThread.quit();
+ }
+
+ @Test
+ public void timeHundredPercentSampling() {
+ mStats.setSamplingInterval(1);
+ runScenario();
+ }
+
+ @Test
+ public void timeOnePercentSampling() {
+ mStats.setSamplingInterval(100);
+ runScenario();
+ }
+
+ @Test
+ public void timeCollectionDisabled() {
+ // We do not collect data on charger.
+ mDeviceState.setCharging(true);
+ runScenario();
+ }
+
+ private void runScenario() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < DISTINCT_MESSAGE_COUNT; i++) {
+ Object token = mStats.messageDispatchStarting();
+ mStats.messageDispatched(token, mMessages[i]);
+ }
+ }
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index 568f4c4..e31f8a0 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -2826,7 +2826,7 @@
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
field public static final int SHOW_MODE_AUTO = 0; // 0x0
field public static final int SHOW_MODE_HIDDEN = 1; // 0x1
- field public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2; // 0x2
+ field public static final int SHOW_MODE_IGNORE_HARD_KEYBOARD = 2; // 0x2
}
public static abstract class AccessibilityService.GestureResultCallback {
@@ -5680,6 +5680,7 @@
public class NotificationManager {
method public java.lang.String addAutomaticZenRule(android.app.AutomaticZenRule);
method public boolean areNotificationsEnabled();
+ method public boolean canNotifyAsPackage(java.lang.String);
method public void cancel(int);
method public void cancel(java.lang.String, int);
method public void cancelAll();
@@ -5698,13 +5699,17 @@
method public android.app.NotificationChannelGroup getNotificationChannelGroup(java.lang.String);
method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
+ method public java.lang.String getNotificationDelegate();
method public android.app.NotificationManager.Policy getNotificationPolicy();
method public boolean isNotificationListenerAccessGranted(android.content.ComponentName);
method public boolean isNotificationPolicyAccessGranted();
method public void notify(int, android.app.Notification);
method public void notify(java.lang.String, int, android.app.Notification);
+ method public void notifyAsPackage(java.lang.String, java.lang.String, int, android.app.Notification);
method public boolean removeAutomaticZenRule(java.lang.String);
+ method public void revokeNotificationDelegate();
method public final void setInterruptionFilter(int);
+ method public void setNotificationDelegate(java.lang.String);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_APP_BLOCK_STATE_CHANGED = "android.app.action.APP_BLOCK_STATE_CHANGED";
@@ -11313,6 +11318,7 @@
field public static final java.lang.String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
field public static final java.lang.String FEATURE_EMBEDDED = "android.hardware.type.embedded";
field public static final java.lang.String FEATURE_ETHERNET = "android.hardware.ethernet";
+ field public static final java.lang.String FEATURE_FACE = "android.hardware.face";
field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
@@ -11322,6 +11328,7 @@
field public static final java.lang.String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
+ field public static final java.lang.String FEATURE_IRIS = "android.hardware.iris";
field public static final java.lang.String FEATURE_LEANBACK = "android.software.leanback";
field public static final java.lang.String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
field public static final java.lang.String FEATURE_LIVE_TV = "android.software.live_tv";
@@ -13784,7 +13791,7 @@
field public static final int YV12 = 842094169; // 0x32315659
}
- public class Insets {
+ public final class Insets {
method public static android.graphics.Insets of(int, int, int, int);
method public static android.graphics.Insets of(android.graphics.Rect);
field public static final android.graphics.Insets NONE;
@@ -13976,6 +13983,7 @@
method public float getFontSpacing();
method public java.lang.String getFontVariationSettings();
method public int getHinting();
+ method public int getHyphenEdit();
method public float getLetterSpacing();
method public android.graphics.MaskFilter getMaskFilter();
method public int getOffsetForAdvance(char[], int, int, int, int, boolean, float);
@@ -13984,6 +13992,8 @@
method public float getRunAdvance(char[], int, int, int, int, boolean, int);
method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int);
method public android.graphics.Shader getShader();
+ method public float getStrikeThruPosition();
+ method public float getStrikeThruThickness();
method public android.graphics.Paint.Cap getStrokeCap();
method public android.graphics.Paint.Join getStrokeJoin();
method public float getStrokeMiter();
@@ -14004,6 +14014,9 @@
method public int getTextWidths(java.lang.String, int, int, float[]);
method public int getTextWidths(java.lang.String, float[]);
method public android.graphics.Typeface getTypeface();
+ method public float getUnderlinePosition();
+ method public float getUnderlineThickness();
+ method public float getWordSpacing();
method public android.graphics.Xfermode getXfermode();
method public boolean hasGlyph(java.lang.String);
method public final boolean isAntiAlias();
@@ -14034,6 +14047,7 @@
method public void setFontFeatureSettings(java.lang.String);
method public boolean setFontVariationSettings(java.lang.String);
method public void setHinting(int);
+ method public void setHyphenEdit(int);
method public void setLetterSpacing(float);
method public void setLinearText(boolean);
method public android.graphics.MaskFilter setMaskFilter(android.graphics.MaskFilter);
@@ -14055,6 +14069,7 @@
method public void setTextSkewX(float);
method public android.graphics.Typeface setTypeface(android.graphics.Typeface);
method public void setUnderlineText(boolean);
+ method public void setWordSpacing(float);
method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
field public static final int DEV_KERN_TEXT_FLAG = 256; // 0x100
@@ -14571,6 +14586,14 @@
method public android.graphics.Typeface.Builder setWeight(int);
}
+ public static class Typeface.CustomFallbackBuilder {
+ ctor public Typeface.CustomFallbackBuilder(android.graphics.fonts.FontFamily);
+ method public android.graphics.Typeface build();
+ method public android.graphics.Typeface.CustomFallbackBuilder setFallback(java.lang.String);
+ method public android.graphics.Typeface.CustomFallbackBuilder setItalic(boolean);
+ method public android.graphics.Typeface.CustomFallbackBuilder setWeight(int);
+ }
+
public class Xfermode {
ctor public Xfermode();
}
@@ -15135,9 +15158,9 @@
public class StateListDrawable extends android.graphics.drawable.DrawableContainer {
ctor public StateListDrawable();
method public void addState(int[], android.graphics.drawable.Drawable);
+ method public int findStateDrawableIndex(int[]);
method public int getStateCount();
method public android.graphics.drawable.Drawable getStateDrawable(int);
- method public int getStateDrawableIndex(int[]);
method public int[] getStateSet(int);
}
@@ -15210,6 +15233,9 @@
public final class Font {
method public android.graphics.fonts.FontVariationAxis[] getAxes();
+ method public java.nio.ByteBuffer getBuffer();
+ method public java.io.File getFile();
+ method public android.os.LocaleList getLocaleList();
method public int getTtcIndex();
method public int getWeight();
method public boolean isItalic();
@@ -15218,7 +15244,9 @@
field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320
field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8
field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c
+ field public static final int FONT_WEIGHT_MAX = 1000; // 0x3e8
field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
+ field public static final int FONT_WEIGHT_MIN = 1; // 0x1
field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258
field public static final int FONT_WEIGHT_THIN = 100; // 0x64
@@ -15258,6 +15286,10 @@
method public static java.lang.String toFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
}
+ public class SystemFonts {
+ method public static java.util.Set<android.graphics.fonts.Font> getAvailableFonts();
+ }
+
}
package android.graphics.pdf {
@@ -39629,10 +39661,12 @@
method public int getId();
method public java.lang.String getKey();
method public android.app.Notification getNotification();
+ method public java.lang.String getOpPkg();
method public java.lang.String getOverrideGroupKey();
method public java.lang.String getPackageName();
method public long getPostTime();
method public java.lang.String getTag();
+ method public int getUid();
method public android.os.UserHandle getUser();
method public deprecated int getUserId();
method public boolean isClearable();
@@ -41867,6 +41901,9 @@
field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
+ field public static final java.lang.String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL = "call_barring_supports_deactivate_all_bool";
+ field public static final java.lang.String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL = "call_barring_supports_password_change_bool";
+ field public static final java.lang.String KEY_CALL_BARRING_VISIBILITY_BOOL = "call_barring_visibility_bool";
field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
field public static final java.lang.String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS = "carrier_data_call_permanent_failure_strings";
@@ -42599,6 +42636,7 @@
}
public class SubscriptionManager {
+ method public void addOnOpportunisticSubscriptionsChangedListener(java.util.concurrent.Executor, android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public boolean canManageSubscription(android.telephony.SubscriptionInfo);
method public static deprecated android.telephony.SubscriptionManager from(android.content.Context);
@@ -42612,8 +42650,11 @@
method public static int getDefaultSmsSubscriptionId();
method public static int getDefaultSubscriptionId();
method public static int getDefaultVoiceSubscriptionId();
+ method public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(int);
+ method public static int[] getSubscriptionIds(int);
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public boolean isNetworkRoaming(int);
+ method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
method public void setSubscriptionOverrideCongested(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
@@ -42629,6 +42670,11 @@
field public static final int INVALID_SUBSCRIPTION_ID = -1; // 0xffffffff
}
+ public static class SubscriptionManager.OnOpportunisticSubscriptionsChangedListener {
+ ctor public SubscriptionManager.OnOpportunisticSubscriptionsChangedListener();
+ method public void onOpportunisticSubscriptionsChanged();
+ }
+
public static class SubscriptionManager.OnSubscriptionsChangedListener {
ctor public SubscriptionManager.OnSubscriptionsChangedListener();
method public void onSubscriptionsChanged();
@@ -43462,6 +43508,22 @@
method public abstract void handleTag(boolean, java.lang.String, android.text.Editable, org.xml.sax.XMLReader);
}
+ public class Hyphenator {
+ method public static int packHyphenEdit(int, int);
+ method public static int unpackEndHyphenEdit(int);
+ method public static int unpackStartHyphenEdit(int);
+ field public static final int END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN = 3; // 0x3
+ field public static final int END_HYPHEN_EDIT_INSERT_HYPHEN = 2; // 0x2
+ field public static final int END_HYPHEN_EDIT_INSERT_MAQAF = 4; // 0x4
+ field public static final int END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN = 5; // 0x5
+ field public static final int END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN = 6; // 0x6
+ field public static final int END_HYPHEN_EDIT_NO_EDIT = 0; // 0x0
+ field public static final int END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN = 1; // 0x1
+ field public static final int START_HYPHEN_EDIT_INSERT_HYPHEN = 1; // 0x1
+ field public static final int START_HYPHEN_EDIT_INSERT_ZWJ = 2; // 0x2
+ field public static final int START_HYPHEN_EDIT_NO_EDIT = 0; // 0x0
+ }
+
public abstract interface InputFilter {
method public abstract java.lang.CharSequence filter(java.lang.CharSequence, int, int, android.text.Spanned, int, int);
}
@@ -44486,6 +44548,11 @@
method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
}
+ public static class LineHeightSpan.Standard implements android.text.style.LineHeightSpan {
+ ctor public LineHeightSpan.Standard(int);
+ method public void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt);
+ }
+
public static abstract interface LineHeightSpan.WithDensity implements android.text.style.LineHeightSpan {
method public abstract void chooseHeight(java.lang.CharSequence, int, int, int, int, android.graphics.Paint.FontMetricsInt, android.text.TextPaint);
}
@@ -45976,6 +46043,7 @@
method public float getDimension(android.util.DisplayMetrics);
method public final float getFloat();
method public float getFraction(float, float);
+ method public boolean isColorType();
method public void setTo(android.util.TypedValue);
field public static final int COMPLEX_MANTISSA_MASK = 16777215; // 0xffffff
field public static final int COMPLEX_MANTISSA_SHIFT = 8; // 0x8
diff --git a/api/system-current.txt b/api/system-current.txt
index 1d5f586..1e1c621 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -474,9 +474,6 @@
method public android.app.Vr2dDisplayProperties.Builder setEnabled(boolean);
}
- public static abstract class Vr2dDisplayProperties.Vr2dDisplayFlag implements java.lang.annotation.Annotation {
- }
-
public class VrManager {
method public int getVr2dDisplayId();
method public boolean isPersistentVrModeEnabled();
@@ -1118,6 +1115,7 @@
public abstract class PackageManager {
method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+ method public abstract boolean arePermissionsIndividuallyControlled();
method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
method public android.content.pm.dex.ArtManager getArtManager();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
@@ -1269,6 +1267,14 @@
}
+package android.graphics.drawable {
+
+ public final class Icon implements android.os.Parcelable {
+ method public static android.graphics.drawable.Icon createWithResource(android.content.res.Resources, int);
+ }
+
+}
+
package android.hardware {
public final class Sensor {
@@ -2618,6 +2624,7 @@
method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
method public void flushGnssBatch();
method public int getGnssBatchSize();
+ method public java.lang.String getNetworkProviderPackage();
method public boolean isLocationEnabledForUser(android.os.UserHandle);
method public boolean isProviderEnabledForUser(java.lang.String, android.os.UserHandle);
method public boolean registerGnssBatchedLocationCallback(long, boolean, android.location.BatchedLocationCallback, android.os.Handler);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index b566099..3fae586 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -43,3 +43,25 @@
}
+// ==== java proto device library (for test only) ==============================
+java_library {
+ name: "statsdprotolite",
+ proto: {
+ type: "lite",
+ include_dirs: ["external/protobuf/src"],
+ },
+
+ srcs: [
+ "src/stats_log.proto",
+ "src/statsd_config.proto",
+ "src/atoms.proto",
+ ],
+
+ static_libs: [
+ "platformprotoslite",
+ ],
+ // Protos have lots of MissingOverride and similar.
+ errorprone: {
+ javacflags: ["-XepDisableAllChecks"],
+ },
+}
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index ba2aaad8..5e87ef6 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -243,28 +243,6 @@
include $(BUILD_NATIVE_TEST)
##############################
-# stats proto static java lib
-##############################
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := statsdprotolite
-
-LOCAL_SRC_FILES := \
- src/stats_log.proto \
- src/statsd_config.proto \
- src/atoms.proto
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- platformprotoslite
-
-LOCAL_PROTOC_FLAGS := \
- -Iexternal/protobuf/src
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-##############################
# statsd micro benchmark
##############################
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 3c9f7ee..8f3ad9d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -130,10 +130,11 @@
GenericAtom generic_atom = 82;
KeyValuePairsAtom key_value_pairs_atom = 83;
VibratorStateChanged vibrator_state_changed = 84;
+ DeferredJobStatsReported deferred_job_stats_reported = 85;
}
// Pulled events will start at field 10000.
- // Next: 10024
+ // Next: 10025
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -153,12 +154,17 @@
SystemUptime system_uptime = 10015;
CpuActiveTime cpu_active_time = 10016;
CpuClusterTime cpu_cluster_time = 10017;
- DiskSpace disk_space = 10018;
+ DiskSpace disk_space = 10018 [deprecated=true];
RemainingBatteryCapacity remaining_battery_capacity = 10019;
FullBatteryCapacity full_battery_capacity = 10020;
Temperature temperature = 10021;
BinderCalls binder_calls = 10022;
BinderCallsExceptions binder_calls_exceptions = 10023;
+ LooperStats looper_stats = 10024;
+ DiskStats disk_stats = 10025;
+ DirectoryUsage directory_usage = 10026;
+ AppSize app_size = 10027;
+ CategorySize category_size = 10028;
}
// DO NOT USE field numbers above 100,000 in AOSP. Field numbers above
@@ -468,6 +474,22 @@
optional State state = 3;
}
+/*
+ * Deferred job stats.
+ *
+ * Logged from:
+ * frameworks/base/services/core/java/com/android/server/job/JobSchedulerService.java
+*/
+message DeferredJobStatsReported {
+ repeated AttributionNode attribution_node = 1;
+
+ // Number of jobs deferred.
+ optional int32 num_jobs_deferred = 2;
+
+ // Time since the last job runs.
+ optional int64 time_since_last_job_millis = 3;
+}
+
/**
* Logs when a job scheduler job state changes.
*
@@ -736,7 +758,7 @@
* Logs kernel wakeup reasons and aborts.
*
* Logged from:
- * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
*/
message KernelWakeupReported {
// Name of the kernel wakeup reason (or abort).
@@ -2203,3 +2225,129 @@
// Total number of exceptions.
optional int64 exception_count = 2;
}
+
+message LooperStats {
+ // Currently not collected and always set to 0.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // Fully qualified class name of the handler target class.
+ //
+ // This field does not contain PII. This is a system server class name.
+ optional string handler_class_name = 2;
+
+ // The name of the thread that runs the Looper.
+ //
+ // This field does not contain PII. This is a system server thread name.
+ optional string looper_thread_name = 3;
+
+ // The name of the dispatched message.
+ //
+ // This field does not contain PII. This is a system server constant or class
+ // name.
+ optional string message_name = 4;
+
+ // Total number of successfully dispatched messages.
+ optional int64 message_count = 5;
+
+ // Total number of messages that failed dispatching.
+ optional int64 exception_count = 6;
+
+ // Total number of processed messages we have data recorded for. If we
+ // collected data for all the messages, message_count will be equal to
+ // recorded_message_count.
+ //
+ // If recorded_message_count is different than message_count, it means data
+ // collection has been sampled. All the fields below will be sampled in this
+ // case.
+ optional int64 recorded_message_count = 7;
+
+ // Total latency of all processed messages.
+ // Average can be computed using recorded_total_latency_micros /
+ // recorded_message_count.
+ optional int64 recorded_total_latency_micros = 8;
+
+ // Total CPU usage of all processed message.
+ // Average can be computed using recorded_total_cpu_micros /
+ // recorded_message_count. Total can be computed using
+ // recorded_total_cpu_micros / recorded_message_count * call_count.
+ optional int64 recorded_total_cpu_micros = 9;
+}
+
+/**
+ * Pulls disk information, such as write speed and latency.
+ */
+message DiskStats {
+ // Time taken to open, write 512B to, and close a file.
+ // -1 if error performing the check.
+ optional int64 data_write_latency_millis = 1;
+
+ optional bool file_based_encryption = 2;
+
+ // Recent disk write speed in kB/s.
+ // -1 if error querying storageed.
+ // 0 if data is unavailable.
+ optional int32 recent_disk_write_speed = 3;
+}
+
+
+/**
+ * Free and total bytes of the Data, Cache, and System partition.
+ */
+message DirectoryUsage {
+ enum Directory {
+ UNKNOWN = 0;
+ DATA = 1;
+ CACHE = 2;
+ SYSTEM = 3;
+ }
+ optional Directory directory = 1;
+ optional int64 free_bytes = 2;
+ optional int64 total_bytes = 3;
+}
+
+
+/**
+ * Size of an application: apk size, data size, and cache size.
+ * Reads from a cached file produced daily by DiskStatsLoggingService.java.
+ * Information is only reported for apps with the primary user (user 0).
+ * Sizes are aggregated by package name.
+ */
+message AppSize {
+ // Including uids will involve modifying diskstats logic.
+ optional string package_name = 1;
+ // App size in bytes. -1 if unavailable.
+ optional int64 app_size_bytes = 2;
+ // App data size in bytes. -1 if unavailable.
+ optional int64 app_data_size_bytes = 3;
+ // App cache size in bytes. -1 if unavailable.
+ optional int64 app_cache_size_bytes = 4;
+ // Time that the cache file was produced.
+ // Uses System.currentTimeMillis(), which is wall clock time.
+ optional int64 cache_time_millis = 5;
+}
+
+
+/**
+ * Size of a particular category. Eg: photos, videos.
+ * Reads from a cached file produced daily by DiskStatsLoggingService.java.
+ */
+message CategorySize {
+ enum Category {
+ UNKNOWN = 0;
+ APP_SIZE = 1;
+ APP_DATA_SIZE = 2;
+ APP_CACHE_SIZE = 3;
+ PHOTOS = 4;
+ VIDEOS = 5;
+ AUDIO = 6;
+ DOWNLOADS = 7;
+ SYSTEM = 8;
+ OTHER = 9;
+ }
+ optional Category category = 1;
+ // Category size in bytes.
+ optional int64 size_bytes = 2;
+ // Time that the cache file was produced.
+ // Uses System.currentTimeMillis(), which is wall clock time.
+ optional int64 cache_time_millis = 3;
+}
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index e44351b..42cc543 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -105,9 +105,9 @@
readPipe.reset(); // Close the read end (owned by the child process).
- // Using fopen() because fwrite() has the right logic to chunking write()
+ // Using fdopen() because fwrite() has the right logic to chunking write()
// over a pipe (see __sfvwrite()).
- FILE* writePipeStream = fdopen(writePipe.get(), "wb");
+ FILE* writePipeStream = android::base::Fdopen(std::move(writePipe), "wb");
if (!writePipeStream) {
ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
return false;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index e6e8455..dbc13dc 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -149,9 +149,6 @@
// system_uptime
{android::util::SYSTEM_UPTIME,
{{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
- // disk_space
- {android::util::DISK_SPACE,
- {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
// remaining_battery_capacity
{android::util::REMAINING_BATTERY_CAPACITY,
{{},
@@ -183,7 +180,37 @@
{{},
{},
1 * NS_PER_SEC,
- new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}}
+ new StatsCompanionServicePuller(android::util::BINDER_CALLS_EXCEPTIONS)}},
+ // looper_stats
+ {android::util::LOOPER_STATS,
+ {{5, 6, 7, 8, 9},
+ {2, 3, 4},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::LOOPER_STATS)}},
+ // Disk Stats
+ {android::util::DISK_STATS,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DISK_STATS)}},
+ // Directory usage
+ {android::util::DIRECTORY_USAGE,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::DIRECTORY_USAGE)}},
+ // Size of app's code, data, and cache
+ {android::util::APP_SIZE,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::APP_SIZE)}},
+ // Size of specific categories of files. Eg. Music.
+ {android::util::CATEGORY_SIZE,
+ {{},
+ {},
+ 1 * NS_PER_SEC,
+ new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}},
};
StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 73e6572..cf04ee3 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -175,6 +175,56 @@
return false;
}
+
+
+bool LogEvent::writeKeyValuePairs(const std::map<int32_t, int64_t>& int_map,
+ const std::map<int32_t, std::string>& string_map,
+ const std::map<int32_t, float>& float_map) {
+ if (mContext) {
+ if (android_log_write_list_begin(mContext) < 0) {
+ return false;
+ }
+ for (const auto& itr : int_map) {
+ if (android_log_write_list_begin(mContext) < 0) {
+ return false;
+ }
+ write(itr.first);
+ write(itr.second);
+ if (android_log_write_list_end(mContext) < 0) {
+ return false;
+ }
+ }
+
+ for (const auto& itr : string_map) {
+ if (android_log_write_list_begin(mContext) < 0) {
+ return false;
+ }
+ write(itr.first);
+ write(itr.second.c_str());
+ if (android_log_write_list_end(mContext) < 0) {
+ return false;
+ }
+ }
+
+ for (const auto& itr : float_map) {
+ if (android_log_write_list_begin(mContext) < 0) {
+ return false;
+ }
+ write(itr.first);
+ write(itr.second);
+ if (android_log_write_list_end(mContext) < 0) {
+ return false;
+ }
+ }
+
+ if (android_log_write_list_end(mContext) < 0) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
bool LogEvent::write(const std::vector<AttributionNodeInternal>& nodes) {
if (mContext) {
if (android_log_write_list_begin(mContext) < 0) {
@@ -225,6 +275,7 @@
int i = 0;
int depth = -1;
int pos[] = {1, 1, 1};
+ bool isKeyValuePairAtom = false;
do {
elem = android_log_read_next(context);
switch ((int)elem.type) {
@@ -232,6 +283,7 @@
// elem at [0] is EVENT_TYPE_LIST, [1] is the timestamp, [2] is tag id.
if (i == 2) {
mTagId = elem.data.int32;
+ isKeyValuePairAtom = (mTagId == android::util::KEY_VALUE_PAIRS_ATOM);
} else {
if (depth < 0 || depth > 2) {
return;
@@ -249,6 +301,11 @@
return;
}
+ // Handles the oneof field in KeyValuePair atom.
+ if (isKeyValuePairAtom && depth == 2) {
+ pos[depth] = 4;
+ }
+
mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32)));
pos[depth]++;
@@ -260,6 +317,10 @@
return;
}
+ // Handles the oneof field in KeyValuePair atom.
+ if (isKeyValuePairAtom && depth == 2) {
+ pos[depth] = 3;
+ }
mValues.push_back(FieldValue(Field(mTagId, pos, depth),
Value(string(elem.data.string, elem.len))));
@@ -274,6 +335,10 @@
ALOGE("Depth > 2. Not supported!");
return;
}
+ // Handles the oneof field in KeyValuePair atom.
+ if (isKeyValuePairAtom && depth == 2) {
+ pos[depth] = 2;
+ }
mValues.push_back(
FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64)));
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 9ed09dd..2ee6bdf 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -119,6 +119,9 @@
bool write(float value);
bool write(const std::vector<AttributionNodeInternal>& nodes);
bool write(const AttributionNodeInternal& node);
+ bool writeKeyValuePairs(const std::map<int32_t, int64_t>& int_map,
+ const std::map<int32_t, std::string>& string_map,
+ const std::map<int32_t, float>& float_map);
/**
* Return a string representation of this event.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 284c451..bcfcd7a 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -367,13 +367,26 @@
}
std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
+ std::shared_ptr<vector<FieldValue>> gaugeFields;
if (mFieldMatchers.size() > 0) {
- std::shared_ptr<vector<FieldValue>> gaugeFields = std::make_shared<vector<FieldValue>>();
+ gaugeFields = std::make_shared<vector<FieldValue>>();
filterGaugeValues(mFieldMatchers, event.getValues(), gaugeFields.get());
- return gaugeFields;
} else {
- return std::make_shared<vector<FieldValue>>(event.getValues());
+ gaugeFields = std::make_shared<vector<FieldValue>>(event.getValues());
}
+ // Trim all dimension fields from output. Dimensions will appear in output report and will
+ // benefit from dictionary encoding. For large pulled atoms, this can give the benefit of
+ // optional repeated field.
+ for (const auto& field : mDimensionsInWhat) {
+ for (auto it = gaugeFields->begin(); it != gaugeFields->end();) {
+ if (it->mField.matches(field)) {
+ it = gaugeFields->erase(it);
+ } else {
+ it++;
+ }
+ }
+ }
+ return gaugeFields;
}
void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 15be1d7..e3da5db 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -175,6 +175,7 @@
FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
FRIEND_TEST(GaugeMetricProducerTest, TestPullOnTrigger);
+ FRIEND_TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput);
};
} // namespace statsd
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index acfa151..6e3b04c 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -89,6 +89,89 @@
EXPECT_EQ((float)1.1, item7.mValue.float_value);
}
+TEST(LogEventTest, TestKeyValuePairsAtomParsing) {
+ LogEvent event1(83, 2000);
+ std::map<int32_t, int64_t> int_map;
+ std::map<int32_t, std::string> string_map;
+ std::map<int32_t, float> float_map;
+
+ int_map[11] = 123L;
+ int_map[22] = 345L;
+
+ string_map[1] = "test2";
+ string_map[2] = "test1";
+
+ float_map[111] = 2.2f;
+ float_map[222] = 1.1f;
+
+ EXPECT_TRUE(event1.writeKeyValuePairs(int_map, string_map, float_map));
+ event1.init();
+
+ EXPECT_EQ(83, event1.GetTagId());
+ const auto& items = event1.getValues();
+ EXPECT_EQ((size_t)12, items.size());
+
+ const FieldValue& item0 = event1.getValues()[0];
+ EXPECT_EQ(0x2010101, item0.mField.getField());
+ EXPECT_EQ(Type::INT, item0.mValue.getType());
+ EXPECT_EQ(11, item0.mValue.int_value);
+
+ const FieldValue& item1 = event1.getValues()[1];
+ EXPECT_EQ(0x2010182, item1.mField.getField());
+ EXPECT_EQ(Type::LONG, item1.mValue.getType());
+ EXPECT_EQ(123L, item1.mValue.long_value);
+
+ const FieldValue& item2 = event1.getValues()[2];
+ EXPECT_EQ(0x2010201, item2.mField.getField());
+ EXPECT_EQ(Type::INT, item2.mValue.getType());
+ EXPECT_EQ(22, item2.mValue.int_value);
+
+ const FieldValue& item3 = event1.getValues()[3];
+ EXPECT_EQ(0x2010282, item3.mField.getField());
+ EXPECT_EQ(Type::LONG, item3.mValue.getType());
+ EXPECT_EQ(345L, item3.mValue.long_value);
+
+ const FieldValue& item4 = event1.getValues()[4];
+ EXPECT_EQ(0x2010301, item4.mField.getField());
+ EXPECT_EQ(Type::INT, item4.mValue.getType());
+ EXPECT_EQ(1, item4.mValue.int_value);
+
+ const FieldValue& item5 = event1.getValues()[5];
+ EXPECT_EQ(0x2010383, item5.mField.getField());
+ EXPECT_EQ(Type::STRING, item5.mValue.getType());
+ EXPECT_EQ("test2", item5.mValue.str_value);
+
+ const FieldValue& item6 = event1.getValues()[6];
+ EXPECT_EQ(0x2010401, item6.mField.getField());
+ EXPECT_EQ(Type::INT, item6.mValue.getType());
+ EXPECT_EQ(2, item6.mValue.int_value);
+
+ const FieldValue& item7 = event1.getValues()[7];
+ EXPECT_EQ(0x2010483, item7.mField.getField());
+ EXPECT_EQ(Type::STRING, item7.mValue.getType());
+ EXPECT_EQ("test1", item7.mValue.str_value);
+
+ const FieldValue& item8 = event1.getValues()[8];
+ EXPECT_EQ(0x2010501, item8.mField.getField());
+ EXPECT_EQ(Type::INT, item8.mValue.getType());
+ EXPECT_EQ(111, item8.mValue.int_value);
+
+ const FieldValue& item9 = event1.getValues()[9];
+ EXPECT_EQ(0x2010584, item9.mField.getField());
+ EXPECT_EQ(Type::FLOAT, item9.mValue.getType());
+ EXPECT_EQ(2.2f, item9.mValue.float_value);
+
+ const FieldValue& item10 = event1.getValues()[10];
+ EXPECT_EQ(0x2018601, item10.mField.getField());
+ EXPECT_EQ(Type::INT, item10.mValue.getType());
+ EXPECT_EQ(222, item10.mValue.int_value);
+
+ const FieldValue& item11 = event1.getValues()[11];
+ EXPECT_EQ(0x2018684, item11.mField.getField());
+ EXPECT_EQ(Type::FLOAT, item11.mValue.getType());
+ EXPECT_EQ(1.1f, item11.mValue.float_value);
+}
+
TEST(LogEventTest, TestLogParsing2) {
LogEvent event1(1, 2000);
@@ -158,7 +241,7 @@
EXPECT_EQ((float)1.1, item7.mValue.float_value);
}
-TEST(LogEventTest, TestKeyValuePairsAtomParsing) {
+TEST(LogEventTest, TestKeyValuePairsEvent) {
std::map<int32_t, int64_t> int_map;
std::map<int32_t, std::string> string_map;
std::map<int32_t, float> float_map;
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index d98395e..ea6eb3f 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -48,6 +48,7 @@
*gaugeMetric->mutable_dimensions_in_what() =
CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
gaugeMetric->set_bucket(FIVE_MINUTES);
+ config.set_hash_strings_in_metric_report(false);
return config;
}
@@ -150,7 +151,7 @@
EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(1, data.bucket_info(1).atom_size());
@@ -159,7 +160,7 @@
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(1, data.bucket_info(2).atom_size());
@@ -168,7 +169,7 @@
data.bucket_info(2).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(1, data.bucket_info(3).atom_size());
@@ -177,7 +178,7 @@
data.bucket_info(3).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(3).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(3).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(3).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(1, data.bucket_info(4).atom_size());
@@ -186,7 +187,7 @@
data.bucket_info(4).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(4).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(4).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(4).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(1, data.bucket_info(5).atom_size());
@@ -195,7 +196,7 @@
data.bucket_info(5).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(5).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(5).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(5).atom(0).temperature().temperature_dc(), 0);
}
@@ -273,7 +274,7 @@
EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(1, data.bucket_info(1).atom_size());
@@ -282,7 +283,7 @@
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(2, data.bucket_info(2).atom_size());
@@ -293,9 +294,9 @@
data.bucket_info(2).elapsed_timestamp_nanos(1));
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
- EXPECT_FALSE(data.bucket_info(2).atom(1).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(2).atom(1).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(2).atom(1).temperature().temperature_dc(), 0);
}
@@ -377,7 +378,7 @@
EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(1, data.bucket_info(1).atom_size());
@@ -386,7 +387,7 @@
EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
EXPECT_EQ(1, data.bucket_info(2).atom_size());
@@ -395,7 +396,7 @@
data.bucket_info(2).elapsed_timestamp_nanos(0));
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+ EXPECT_TRUE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
}
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 9471faa..bf58b9c 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -95,6 +95,8 @@
data->clear();
shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event->write(3);
+ event->write("some value");
+ event->write(11);
event->init();
data->push_back(event);
return true;
@@ -600,9 +602,109 @@
EXPECT_EQ(10, it->mValue.int_value);
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size());
- EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
- EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value);
- EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms[2].mFields->begin()->mValue.int_value);
+ EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()
+ ->second.back()
+ .mGaugeAtoms[0]
+ .mFields->begin()
+ ->mValue.int_value);
+ EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin()
+ ->second.back()
+ .mGaugeAtoms[1]
+ .mFields->begin()
+ ->mValue.int_value);
+ EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin()
+ ->second.back()
+ .mGaugeAtoms[2]
+ .mFields->begin()
+ ->mValue.int_value);
+}
+
+TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
+ GaugeMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_sampling_type(GaugeMetric::ALL_CONDITION_CHANGES);
+ metric.mutable_gauge_fields_filter()->set_include_all(true);
+ auto dimensionMatcher = metric.mutable_dimensions_in_what();
+ // use field 1 as dimension.
+ dimensionMatcher->set_field(tagId);
+ dimensionMatcher->add_child()->set_field(1);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3);
+ event->write(3);
+ event->write(4);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(4);
+ event->write(5);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+ event->write(4);
+ event->write(6);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ int triggerId = 5;
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ LogEvent trigger(triggerId, bucketStartTimeNs + 10);
+ trigger.init();
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
+ EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+ trigger.setElapsedTimestampNs(bucketStartTimeNs + 20);
+ gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
+ EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(4);
+ event->write(11);
+ event->init();
+ allData.push_back(event);
+
+ gaugeProducer.onDataPulled(allData);
+ EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+ auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
+ EXPECT_EQ(INT, it->mValue.getType());
+ EXPECT_EQ(11, it->mValue.int_value);
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size());
+ auto bucketIt = gaugeProducer.mPastBuckets.begin();
+ EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size());
+ EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
+ EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
+ bucketIt++;
+ EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size());
+ EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
+ EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
+ EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value);
}
} // namespace statsd
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index cdcdc46..04b33b1 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -11818,6 +11818,7 @@
HPLlibcore/io/BlockGuardOs;->rename(Ljava/lang/String;Ljava/lang/String;)V
HPLlibcore/io/BlockGuardOs;->sendto(Ljava/io/FileDescriptor;[BIIILjava/net/SocketAddress;)I
HPLlibcore/io/BlockGuardOs;->write(Ljava/io/FileDescriptor;Ljava/nio/ByteBuffer;)I
+HPLlibcore/io/ForwardingOs;->android_fdsan_exchange_owner_tag(Ljava/io/FileDescriptor;JJ)V
HPLlibcore/io/ForwardingOs;->munlock(JJ)V
HPLlibcore/io/ForwardingOs;->munmap(JJ)V
HPLlibcore/io/ForwardingOs;->setsockoptByte(Ljava/io/FileDescriptor;III)V
@@ -51619,6 +51620,7 @@
HSPLlibcore/io/IoUtils$FileReader;-><init>(Ljava/lang/String;)V
HSPLlibcore/io/IoUtils$FileReader;->readFully()Llibcore/io/IoUtils$FileReader;
HSPLlibcore/io/IoUtils$FileReader;->toByteArray()[B
+HSPLlibcore/io/IoUtils;->acquireRawFd(Ljava/io/FileDescriptor;)I
HSPLlibcore/io/IoUtils;->canOpenReadOnly(Ljava/lang/String;)Z
HSPLlibcore/io/IoUtils;->close(Ljava/io/FileDescriptor;)V
HSPLlibcore/io/IoUtils;->closeQuietly(Ljava/io/FileDescriptor;)V
@@ -51626,6 +51628,7 @@
HSPLlibcore/io/IoUtils;->readFileAsByteArray(Ljava/lang/String;)[B
HSPLlibcore/io/IoUtils;->readFileAsString(Ljava/lang/String;)Ljava/lang/String;
HSPLlibcore/io/IoUtils;->setBlocking(Ljava/io/FileDescriptor;Z)V
+HSPLlibcore/io/IoUtils;->setFdOwner(Ljava/io/FileDescriptor;Ljava/lang/Object;)V
HSPLlibcore/io/Linux;->read(Ljava/io/FileDescriptor;[BII)I
HSPLlibcore/io/Linux;->recvfrom(Ljava/io/FileDescriptor;[BIIILjava/net/InetSocketAddress;)I
HSPLlibcore/io/Linux;->sendto(Ljava/io/FileDescriptor;[BIIILjava/net/InetAddress;I)I
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 02719e3f8..45e38cf9 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -152,8 +152,8 @@
Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V
Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V
Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V
-Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;Landroid/view/InputChannel;)V
-Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;)Z
+Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V
+Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z
Landroid/view/IWindowManager;->endProlongedAnimations()V
Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V
Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;Z)V
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 5b73eaa..446e6cc 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -399,7 +399,7 @@
@IntDef(prefix = { "SHOW_MODE_" }, value = {
SHOW_MODE_AUTO,
SHOW_MODE_HIDDEN,
- SHOW_MODE_WITH_HARD_KEYBOARD
+ SHOW_MODE_IGNORE_HARD_KEYBOARD
})
public @interface SoftKeyboardShowMode {}
@@ -419,7 +419,7 @@
* Allow the soft keyboard to be shown, even if a hard keyboard is connected
* @see SoftKeyboardController
*/
- public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2;
+ public static final int SHOW_MODE_IGNORE_HARD_KEYBOARD = 2;
/**
* Mask used to cover the show modes supported in public API
@@ -1205,7 +1205,7 @@
*
* @see AccessibilityService#SHOW_MODE_AUTO
* @see AccessibilityService#SHOW_MODE_HIDDEN
- * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
+ * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD
*/
public static final class SoftKeyboardController {
private final AccessibilityService mService;
@@ -1354,7 +1354,7 @@
*
* @see AccessibilityService#SHOW_MODE_AUTO
* @see AccessibilityService#SHOW_MODE_HIDDEN
- * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
+ * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD
*/
@SoftKeyboardShowMode
public int getShowMode() {
@@ -1384,7 +1384,7 @@
*
* @see AccessibilityService#SHOW_MODE_AUTO
* @see AccessibilityService#SHOW_MODE_HIDDEN
- * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
+ * @see AccessibilityService#SHOW_MODE_IGNORE_HARD_KEYBOARD
*/
public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
final IAccessibilityServiceConnection connection =
diff --git a/core/java/android/accessibilityservice/TEST_MAPPING b/core/java/android/accessibilityservice/TEST_MAPPING
new file mode 100644
index 0000000..c7c4c3a
--- /dev/null
+++ b/core/java/android/accessibilityservice/TEST_MAPPING
@@ -0,0 +1,23 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsAccessibilityServiceTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsAccessibilityServiceTestCases"
+ },
+ {
+ "name": "CtsAccessibilityTestCases"
+ }
+ ]
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bf2d860..041a5b0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1461,7 +1461,7 @@
*/
@Override
public AutofillId autofillClientGetNextAutofillId() {
- return new AutofillId(getNextAutofillId());
+ return new AutofillId(getAutofillManager(), getNextAutofillId());
}
/**
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index ec4c4db..9b13420 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -1151,7 +1151,9 @@
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(AlarmClockInfoProto.TRIGGER_TIME_MS, mTriggerTime);
- mShowIntent.writeToProto(proto, AlarmClockInfoProto.SHOW_INTENT);
+ if (mShowIntent != null) {
+ mShowIntent.writeToProto(proto, AlarmClockInfoProto.SHOW_INTENT);
+ }
proto.end(token);
}
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 3171e3e..4f004d93 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -165,4 +165,9 @@
void applyRestore(in byte[] payload, int user);
ParceledListSlice getAppActiveNotifications(String callingPkg, int userId);
+
+ void setNotificationDelegate(String callingPkg, String delegate);
+ void revokeNotificationDelegate(String callingPkg);
+ String getNotificationDelegate(String callingPkg);
+ boolean canNotifyAsPackage(String callingPkg, String targetPkg);
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 4b25b8b..b96b39d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -352,7 +353,7 @@
}
/**
- * Post a notification to be shown in the status bar. If a notification with
+ * Posts a notification to be shown in the status bar. If a notification with
* the same tag and id has already been posted by your application and has not yet been
* canceled, it will be replaced by the updated information.
*
@@ -376,6 +377,42 @@
}
/**
+ * Posts a notification as a specified package to be shown in the status bar. If a notification
+ * with the same tag and id has already been posted for that package and has not yet been
+ * canceled, it will be replaced by the updated information.
+ *
+ * All {@link android.service.notification.NotificationListenerService listener services} will
+ * be granted {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} access to any {@link Uri uris}
+ * provided on this notification or the
+ * {@link NotificationChannel} this notification is posted to using
+ * {@link Context#grantUriPermission(String, Uri, int)}. Permission will be revoked when the
+ * notification is canceled, or you can revoke permissions with
+ * {@link Context#revokeUriPermission(Uri, int)}.
+ *
+ * @param targetPackage The package to post the notification as. The package must have granted
+ * you access to post notifications on their behalf with
+ * {@link #setNotificationDelegate(String)}.
+ * @param tag A string identifier for this notification. May be {@code null}.
+ * @param id An identifier for this notification. The pair (tag, id) must be unique
+ * within your application.
+ * @param notification A {@link Notification} object describing what to
+ * show the user. Must not be null.
+ */
+ public void notifyAsPackage(@NonNull String targetPackage, @NonNull String tag, int id,
+ Notification notification) {
+ INotificationManager service = getService();
+ String sender = mContext.getPackageName();
+
+ try {
+ if (localLOGV) Log.v(TAG, sender + ": notify(" + id + ", " + notification + ")");
+ service.enqueueNotificationWithTag(targetPackage, sender, tag, id,
+ fixNotification(notification), mContext.getUser().getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -383,6 +420,18 @@
{
INotificationManager service = getService();
String pkg = mContext.getPackageName();
+
+ try {
+ if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
+ service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
+ fixNotification(notification), user.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private Notification fixNotification(Notification notification) {
+ String pkg = mContext.getPackageName();
// Fix the notification as best we can.
Notification.addFieldsFromContext(mContext, notification);
@@ -400,19 +449,12 @@
+ notification);
}
}
- if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
+
notification.reduceImageSizes(mContext);
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLowRam = am.isLowRamDevice();
- final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam,
- mContext);
- try {
- service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
- copy, user.getIdentifier());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ return Builder.maybeCloneStrippedForDelivery(notification, isLowRam, mContext);
}
private void fixLegacySmallIcon(Notification n, String pkg) {
@@ -474,6 +516,72 @@
}
/**
+ * Allows a package to post notifications on your behalf using
+ * {@link #notifyAsPackage(String, String, int, Notification)}.
+ *
+ * This can be used to allow persistent processes to post notifications based on messages
+ * received on your behalf from the cloud, without your process having to wake up.
+ *
+ * You can check if you have an allowed delegate with {@link #getNotificationDelegate()} and
+ * revoke your delegate with {@link #revokeNotificationDelegate()}.
+ *
+ * @param delegate Package name of the app which can send notifications on your behalf.
+ */
+ public void setNotificationDelegate(@NonNull String delegate) {
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
+ try {
+ service.setNotificationDelegate(pkg, delegate);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Revokes permission for your {@link #setNotificationDelegate(String) notification delegate}
+ * to post notifications on your behalf.
+ */
+ public void revokeNotificationDelegate() {
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ try {
+ service.revokeNotificationDelegate(pkg);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the {@link #setNotificationDelegate(String) delegate} that can post notifications on
+ * your behalf, if there currently is one.
+ */
+ public @Nullable String getNotificationDelegate() {
+ INotificationManager service = getService();
+ String pkg = mContext.getPackageName();
+ try {
+ return service.getNotificationDelegate(pkg);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether you are allowed to post notifications on behalf of a given package, with
+ * {@link #notifyAsPackage(String, String, int, Notification)}.
+ *
+ * See {@link #setNotificationDelegate(String)}.
+ */
+ public boolean canNotifyAsPackage(String pkg) {
+ INotificationManager service = getService();
+ try {
+ return service.canNotifyAsPackage(mContext.getPackageName(), pkg);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a group container for {@link NotificationChannel} objects.
*
* This can be used to rename an existing group.
diff --git a/core/java/android/app/Vr2dDisplayProperties.java b/core/java/android/app/Vr2dDisplayProperties.java
index 2fd82b2..1752131 100644
--- a/core/java/android/app/Vr2dDisplayProperties.java
+++ b/core/java/android/app/Vr2dDisplayProperties.java
@@ -35,6 +35,7 @@
public static final int FLAG_VIRTUAL_DISPLAY_ENABLED = 1;
+ /** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
FLAG_VIRTUAL_DISPLAY_ENABLED
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 5fe1af0..beb1c78 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -74,12 +74,10 @@
* that the user can select, via {@link DevicePolicyManager#setPasswordQuality}
* and {@link DevicePolicyManager#setPasswordMinimumLength}.
*
- * <p>To control this policy, the device admin must have a "limit-password"
- * tag in the "uses-policies" section of its meta-data.
- *
- * <p>This policy is deprecated for use by a device admin. In future releases, it will
- * only be possible for a device owner or profile owner to enforce constraints on user
- * passwords.
+ * <p>To control this policy, the device admin must be a device owner or profile owner,
+ * and must have a "limit-password" tag in the "uses-policies" section of its meta-data.
+ * If used by a device owner, the policy only affects the primary user and its profiles,
+ * but not any secondary users on the device.
*/
public static final int USES_POLICY_LIMIT_PASSWORD = 0;
@@ -139,11 +137,10 @@
* A type of policy that this device admin can use: force the user to
* change their password after an administrator-defined time limit.
*
- * <p>To control this policy, the device admin must have an "expire-password"
- * tag in the "uses-policies" section of its meta-data.
- *
- * <p>This policy is deprecated for use by a device admin. In future releases, it will
- * only be possible for a device owner or profile owner to enforce password expiry.
+ * <p>To control this policy, the device admin must be a device owner or profile owner,
+ * and must have an "expire-password" tag in the "uses-policies" section of its meta-data.
+ * If used by a device owner, the policy only affects the primary user and its profiles,
+ * but not any secondary users on the device.
*/
public static final int USES_POLICY_EXPIRE_PASSWORD = 6;
@@ -158,23 +155,19 @@
/**
* A type of policy that this device admin can use: disables use of all device cameras.
*
- * <p>To control this policy, the device admin must have a "disable-camera"
- * tag in the "uses-policies" section of its meta-data.
- *
- * <p>This policy is deprecated for use by a device admin. In future releases, it will
- * only be possible for a device owner or profile owner to disable use of the camera.
+ * <p>To control this policy, the device admin must be a device owner or profile owner,
+ * and must have a "disable-camera" tag in the "uses-policies" section of its meta-data.
+ * If used by a device owner, the policy affects all users on the device.
*/
public static final int USES_POLICY_DISABLE_CAMERA = 8;
/**
* A type of policy that this device admin can use: disables use of keyguard features.
*
- * <p>To control this policy, the device admin must have a "disable-keyguard-features"
- * tag in the "uses-policies" section of its meta-data.
- *
- * <p>This policy is deprecated for use by a device admin. In future releases, it will
- * only be possible for a device owner or profile owner to disable use of keyguard
- * features.
+ * <p>To control this policy, the device admin must be a device owner or profile owner,
+ * and must have a "disable-keyguard-features" tag in the "uses-policies" section of its
+ * meta-data. If used by a device owner, the policy only affects the primary user and
+ * its profiles, but not any secondary users on the device.
*/
public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 173b766..ee6a81d 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -32,6 +32,7 @@
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import com.android.internal.util.Preconditions;
@@ -72,6 +73,8 @@
boolean mHaveData;
ComponentName mActivityComponent;
+ // Not written to parcel, only used to set session id on virtual node children
+ private final int mAutofillSessionId;
private boolean mIsHomeActivity;
private int mFlags;
@@ -1846,11 +1849,13 @@
@Override
public void setAutofillId(@NonNull AutofillId id) {
mNode.mAutofillId = id;
+ mNode.mAutofillId.setSessionId(mAssist.mAutofillSessionId);
}
@Override
public void setAutofillId(@NonNull AutofillId parentId, int virtualId) {
mNode.mAutofillId = new AutofillId(parentId, virtualId);
+ mNode.mAutofillId.setSessionId(mAssist.mAutofillSessionId);
}
@Override
@@ -2040,6 +2045,8 @@
public AssistStructure(Activity activity, boolean forAutoFill, int flags) {
mHaveData = true;
mActivityComponent = activity.getComponentName();
+ final AutofillManager afm = activity.getSystemService(AutofillManager.class);
+ mAutofillSessionId = afm == null ? AutofillManager.NO_SESSION : afm.getSessionId();
mFlags = flags;
ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
activity.getActivityToken());
@@ -2056,6 +2063,7 @@
public AssistStructure() {
mHaveData = true;
mActivityComponent = null;
+ mAutofillSessionId = AutofillManager.NO_SESSION;
mFlags = 0;
}
@@ -2063,6 +2071,7 @@
public AssistStructure(Parcel in) {
mIsHomeActivity = in.readInt() == 1;
mReceiveChannel = in.readStrongBinder();
+ mAutofillSessionId = AutofillManager.NO_SESSION;
}
/**
@@ -2082,6 +2091,10 @@
ensureData();
}
Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString());
+ if (mAutofillSessionId != AutofillManager.NO_SESSION) {
+ Log.i(TAG, "Autofill Session ID: " + mAutofillSessionId);
+ }
+
Log.i(TAG, "Sanitize on write: " + mSanitizeOnWrite);
Log.i(TAG, "Flags: " + mFlags);
final int N = getWindowNodeCount();
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index 53315cc..d148afb 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -400,7 +400,7 @@
that.initialLayout = this.initialLayout;
that.initialKeyguardLayout = this.initialKeyguardLayout;
that.configure = this.configure == null ? null : this.configure.clone();
- that.label = this.label == null ? null : this.label.substring(0);
+ that.label = this.label;
that.icon = this.icon;
that.previewImage = this.previewImage;
that.autoAdvanceViewId = this.autoAdvanceViewId;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1108f93..225b6cf 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -615,6 +615,13 @@
public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
/**
+ * Value for {@link #privateFlags}: whether this app is signed with the
+ * platform key.
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+
+ /**
* Value for {@link #privateFlags}: whether this app is pre-installed on the
* google partition of the system image.
* @hide
@@ -622,11 +629,11 @@
public static final int PRIVATE_FLAG_PRODUCT_SERVICES = 1 << 21;
/**
- * Value for {@link #privateFlags}: whether this app is signed with the
- * platform key.
+ * Indicates whether this package requires access to non-SDK APIs.
+ * Only system apps and tests are allowed to use this property.
* @hide
*/
- public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+ public static final int PRIVATE_FLAG_USES_NON_SDK_API = 1 << 22;
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
@@ -1009,13 +1016,6 @@
public String appComponentFactory;
/**
- * Indicates whether this package requires access to non-SDK APIs. Only system apps
- * and tests are allowed to use this property.
- * @hide
- */
- public boolean usesNonSdkApi;
-
- /**
* The category of this app. Categories are used to cluster multiple apps
* together into meaningful groups, such as when summarizing battery,
* network, or disk usage. Apps should only define this value when they fit
@@ -1294,6 +1294,7 @@
pw.println(prefix + "category=" + category);
}
pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
+ pw.println(prefix + "usesNonSdkApi=" + usesNonSdkApi());
}
super.dumpBack(pw, prefix);
}
@@ -1718,11 +1719,18 @@
return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
}
+ /**
+ * @hide
+ */
+ public boolean usesNonSdkApi() {
+ return (privateFlags & PRIVATE_FLAG_USES_NON_SDK_API) != 0;
+ }
+
private boolean isAllowedToUseHiddenApis() {
if (isSignedWithPlatformKey()) {
return true;
} else if (isSystemApp() || isUpdatedSystemApp()) {
- return usesNonSdkApi || isPackageWhitelistedForHiddenApis();
+ return usesNonSdkApi() || isPackageWhitelistedForHiddenApis();
} else {
return false;
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7d8ff4c..1b4878c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2274,7 +2274,6 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to perform face authentication.
- * @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_FACE = "android.hardware.face";
@@ -2282,7 +2281,6 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
- * @hide
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_IRIS = "android.hardware.iris";
@@ -3278,11 +3276,14 @@
@PermissionInfoFlags int flags) throws NameNotFoundException;
/**
- * Returns true if some permissions are individually controlled
+ * Returns true if some permissions are individually controlled.
+ *
+ * <p>The user usually grants and revokes permission-groups. If this option is set some
+ * dangerous system permissions can be revoked/granted by the user separately from their group.
*
* @hide
*/
- @TestApi
+ @TestApi @SystemApi
public abstract boolean arePermissionsIndividuallyControlled();
/**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8b058dc..6d49362 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3659,8 +3659,10 @@
ai.appComponentFactory = buildClassName(ai.packageName, factory, outError);
}
- ai.usesNonSdkApi = sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_usesNonSdkApi, false);
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_usesNonSdkApi, false)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API;
+ }
if (outError[0] == null) {
CharSequence pname;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index f7aea97..121b432 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1096,7 +1096,9 @@
protoOutputStream.write(FONT_SCALE, fontScale);
protoOutputStream.write(MCC, mcc);
protoOutputStream.write(MNC, mnc);
- mLocaleList.writeToProto(protoOutputStream, LOCALES);
+ if (mLocaleList != null) {
+ mLocaleList.writeToProto(protoOutputStream, LOCALES);
+ }
protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
protoOutputStream.write(COLOR_MODE, colorMode);
protoOutputStream.write(TOUCHSCREEN, touchscreen);
@@ -1111,7 +1113,9 @@
protoOutputStream.write(SCREEN_HEIGHT_DP, screenHeightDp);
protoOutputStream.write(SMALLEST_SCREEN_WIDTH_DP, smallestScreenWidthDp);
protoOutputStream.write(DENSITY_DPI, densityDpi);
- windowConfiguration.writeToProto(protoOutputStream, WINDOW_CONFIGURATION);
+ if (windowConfiguration != null) {
+ windowConfiguration.writeToProto(protoOutputStream, WINDOW_CONFIGURATION);
+ }
protoOutputStream.end(token);
}
@@ -2072,17 +2076,6 @@
break;
}
- switch (config.colorMode & Configuration.COLOR_MODE_HDR_MASK) {
- case Configuration.COLOR_MODE_HDR_YES:
- parts.add("highdr");
- break;
- case Configuration.COLOR_MODE_HDR_NO:
- parts.add("lowdr");
- break;
- default:
- break;
- }
-
switch (config.colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK) {
case Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES:
parts.add("widecg");
@@ -2094,6 +2087,17 @@
break;
}
+ switch (config.colorMode & Configuration.COLOR_MODE_HDR_MASK) {
+ case Configuration.COLOR_MODE_HDR_YES:
+ parts.add("highdr");
+ break;
+ case Configuration.COLOR_MODE_HDR_NO:
+ parts.add("lowdr");
+ break;
+ default:
+ break;
+ }
+
switch (config.orientation) {
case Configuration.ORIENTATION_LANDSCAPE:
parts.add("land");
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index b4955ea..496f34c 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -30,6 +30,12 @@
* @hide Only for use within the system server.
*/
public final class DisplayViewport {
+
+ // Viewport constants defined in InputReader.h.
+ public static final int VIEWPORT_INTERNAL = 1;
+ public static final int VIEWPORT_EXTERNAL = 2;
+ public static final int VIEWPORT_VIRTUAL = 3;
+
// True if this viewport is valid.
public boolean valid;
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 3141be4..ae0855a 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -300,6 +300,23 @@
public static final String EXTRA_PERMISSION_GRANTED = "permission";
/**
+ * Name of extra added to start systemui.usb.UsbPermissionActivity
+ * containing package name of the app which requests USB permission.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PACKAGE = "android.hardware.usb.extra.PACKAGE";
+
+ /**
+ * Name of extra added to start systemui.usb.UsbPermissionActivity
+ * containing the whether the app which requests USB permission can be set as default handler
+ * for USB device attach event or USB accessory attach event or not.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CAN_BE_DEFAULT = "android.hardware.usb.extra.CAN_BE_DEFAULT";
+
+ /**
* Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
* {@hide}
*/
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 2c7e51a..00a1f6f 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -35,6 +35,7 @@
import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputContext;
@@ -60,7 +61,7 @@
private static final String TAG = "InputMethodWrapper";
private static final int DO_DUMP = 1;
- private static final int DO_ATTACH_TOKEN = 10;
+ private static final int DO_INITIALIZE_INTERNAL = 10;
private static final int DO_SET_INPUT_CONTEXT = 20;
private static final int DO_UNSET_INPUT_CONTEXT = 30;
private static final int DO_START_INPUT = 32;
@@ -159,9 +160,15 @@
args.recycle();
return;
}
-
- case DO_ATTACH_TOKEN: {
- inputMethod.attachToken((IBinder)msg.obj);
+
+ case DO_INITIALIZE_INTERNAL: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ inputMethod.initializeInternal((IBinder) args.arg1,
+ (IInputMethodPrivilegedOperations) args.arg2);
+ } finally {
+ args.recycle();
+ }
return;
}
case DO_SET_INPUT_CONTEXT: {
@@ -246,8 +253,9 @@
@BinderThread
@Override
- public void attachToken(IBinder token) {
- mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
+ public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageOO(DO_INITIALIZE_INTERNAL, token, privOps));
}
@BinderThread
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 46671b2..aa5cf09 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -62,7 +62,6 @@
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
-import android.view.WindowManager.BadTokenException;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CursorAnchorInfo;
@@ -80,6 +79,10 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -343,7 +346,8 @@
private static final int BACK_DISPOSITION_MAX = BACK_DISPOSITION_ADJUST_NOTHING;
InputMethodManager mImm;
-
+ private InputMethodPrivilegedOperations mPrivOps = new InputMethodPrivilegedOperations();
+
@UnsupportedAppUsage
int mTheme = 0;
@@ -354,7 +358,6 @@
SoftInputWindow mWindow;
boolean mInitialized;
boolean mWindowCreated;
- boolean mWindowAdded;
boolean mWindowVisible;
boolean mWindowWasVisible;
boolean mInShowWindow;
@@ -448,14 +451,29 @@
public class InputMethodImpl extends AbstractInputMethodImpl {
/**
* {@inheritDoc}
+ * @hide
+ */
+ @MainThread
+ @Override
+ public final void initializeInternal(IBinder token,
+ IInputMethodPrivilegedOperations privilegedOperations) {
+ mPrivOps.set(privilegedOperations);
+ mImm.registerInputMethodPrivOps(token, mPrivOps);
+ attachToken(token);
+ }
+
+ /**
+ * {@inheritDoc}
*/
@MainThread
@Override
public void attachToken(IBinder token) {
- if (mToken == null) {
- mToken = token;
- mWindow.setToken(token);
+ if (mToken != null) {
+ throw new IllegalStateException(
+ "attachToken() must be called at most once. token=" + token);
}
+ mToken = token;
+ mWindow.setToken(token);
}
/**
@@ -470,9 +488,7 @@
mInputConnection = binding.getConnection();
if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding
+ " ic=" + mInputConnection);
- if (mImm != null && mToken != null) {
- mImm.reportFullscreenMode(mToken, mIsFullscreen);
- }
+ reportFullscreenMode();
initialize();
onBindInput();
}
@@ -521,8 +537,7 @@
public void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken) {
- mImm.reportStartInput(mToken, startInputToken);
-
+ mPrivOps.reportStartInput(startInputToken);
// This needs to be dispatched to interface methods rather than doStartInput().
// Otherwise IME developers who have overridden those interface methods will lose
// notifications.
@@ -559,21 +574,12 @@
if (DEBUG) Log.v(TAG, "showSoftInput()");
boolean wasVis = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
- try {
- showWindow(true);
- } catch (BadTokenException e) {
- // We have ignored BadTokenException here since Jelly Bean MR-2 (API Level 18).
- // We could ignore BadTokenException in InputMethodService#showWindow() instead,
- // but it may break assumptions for those who override #showWindow() that we can
- // detect errors in #showWindow() by checking BadTokenException.
- // TODO: Investigate its feasibility. Update JavaDoc of #showWindow() of
- // whether it's OK to override #showWindow() or not.
- }
+ showWindow(true);
}
clearInsetOfPreviousIme();
// If user uses hard keyboard, IME button should always be shown.
- mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()),
- mBackDisposition);
+ setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+
if (resultReceiver != null) {
resultReceiver.send(wasVis != isInputViewShown()
? InputMethodManager.RESULT_SHOWN
@@ -592,6 +598,10 @@
}
}
+ private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
+ mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition);
+ }
+
/**
* Concrete implementation of
* {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
@@ -986,17 +996,16 @@
mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
mInsetsComputer);
doFinishInput();
- if (mWindowAdded) {
- // Disable exit animation for the current IME window
- // to avoid the race condition between the exit and enter animations
- // when the current IME is being switched to another one.
- mWindow.getWindow().setWindowAnimations(0);
- mWindow.dismiss();
- }
+ mWindow.dismissForDestroyIfNecessary();
if (mSettingsObserver != null) {
mSettingsObserver.unregister();
mSettingsObserver = null;
}
+ if (mToken != null) {
+ // This is completely optional, but allows us to show more explicit error messages
+ // when IME developers are doing something unsupported.
+ mImm.unregisterInputMethodPrivOps(mToken);
+ }
}
/**
@@ -1054,8 +1063,7 @@
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
- mImm.setImeWindowStatus(mToken, IME_ACTIVE | (showing ? IME_VISIBLE : 0),
- mBackDisposition);
+ setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition);
}
}
@@ -1105,7 +1113,7 @@
return;
}
mBackDisposition = disposition;
- mImm.setImeWindowStatus(mToken, mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
+ setImeWindowStatus(mapToImeWindowStatus(isInputViewShown()), mBackDisposition);
}
/**
@@ -1168,7 +1176,7 @@
* used input method and subtype.
*/
public final boolean switchToPreviousInputMethod() {
- return mImm.switchToPreviousInputMethodInternal(mToken);
+ return mPrivOps.switchToPreviousInputMethod();
}
/**
@@ -1180,7 +1188,7 @@
* input method and subtype.
*/
public final boolean switchToNextInputMethod(boolean onlyCurrentIme) {
- return mImm.switchToNextInputMethodInternal(mToken, onlyCurrentIme);
+ return mPrivOps.switchToNextInputMethod(onlyCurrentIme);
}
/**
@@ -1193,7 +1201,7 @@
* between IMEs and subtypes.
*/
public final boolean shouldOfferSwitchingToNextInputMethod() {
- return mImm.shouldOfferSwitchingToNextInputMethodInternal(mToken);
+ return mPrivOps.shouldOfferSwitchingToNextInputMethod();
}
public boolean getCurrentInputStarted() {
@@ -1203,7 +1211,11 @@
public EditorInfo getCurrentInputEditorInfo() {
return mInputEditorInfo;
}
-
+
+ private void reportFullscreenMode() {
+ mPrivOps.reportFullscreenMode(mIsFullscreen);
+ }
+
/**
* Re-evaluate whether the input method should be running in fullscreen
* mode, and update its UI if this has changed since the last time it
@@ -1218,9 +1230,7 @@
if (mIsFullscreen != isFullscreen || !mFullscreenApplied) {
changed = true;
mIsFullscreen = isFullscreen;
- if (mImm != null && mToken != null) {
- mImm.reportFullscreenMode(mToken, mIsFullscreen);
- }
+ reportFullscreenMode();
mFullscreenApplied = true;
initialize();
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
@@ -1509,12 +1519,12 @@
public void showStatusIcon(@DrawableRes int iconResId) {
mStatusIcon = iconResId;
- mImm.showStatusIconInternal(mToken, getPackageName(), iconResId);
+ mPrivOps.updateStatusIcon(getPackageName(), iconResId);
}
public void hideStatusIcon() {
mStatusIcon = 0;
- mImm.hideStatusIconInternal(mToken);
+ mPrivOps.updateStatusIcon(null, 0);
}
/**
@@ -1525,7 +1535,7 @@
* @param id Unique identifier of the new input method to start.
*/
public void switchInputMethod(String id) {
- mImm.setInputMethodInternal(mToken, id);
+ mPrivOps.setInputMethod(id);
}
/**
@@ -1537,7 +1547,7 @@
* @param subtype The new subtype of the new input method to be switched to.
*/
public final void switchInputMethod(String id, InputMethodSubtype subtype) {
- mImm.setInputMethodAndSubtypeInternal(mToken, id, subtype);
+ mPrivOps.setInputMethodAndSubtype(id, subtype);
}
public void setExtractView(View view) {
@@ -1778,7 +1788,6 @@
public void showWindow(boolean showInput) {
if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
+ " mShowInputRequested=" + mShowInputRequested
- + " mWindowAdded=" + mWindowAdded
+ " mWindowCreated=" + mWindowCreated
+ " mWindowVisible=" + mWindowVisible
+ " mInputStarted=" + mInputStarted
@@ -1788,27 +1797,12 @@
Log.w(TAG, "Re-entrance in to showWindow");
return;
}
-
- try {
- mWindowWasVisible = mWindowVisible;
- mInShowWindow = true;
- showWindowInner(showInput);
- } catch (BadTokenException e) {
- // BadTokenException is a normal consequence in certain situations, e.g., swapping IMEs
- // while there is a DO_SHOW_SOFT_INPUT message in the IIMethodWrapper queue.
- if (DEBUG) Log.v(TAG, "BadTokenException: IME is done.");
- mWindowVisible = false;
- mWindowAdded = false;
- // Rethrow the exception to preserve the existing behavior. Some IMEs may have directly
- // called this method and relied on this exception for some clean-up tasks.
- // TODO: Give developers a clear guideline of whether it's OK to call this method or
- // InputMethodService#requestShowSelf(int) should always be used instead.
- throw e;
- } finally {
- // TODO: Is it OK to set true when we get BadTokenException?
- mWindowWasVisible = true;
- mInShowWindow = false;
- }
+
+ mWindowWasVisible = mWindowVisible;
+ mInShowWindow = true;
+ showWindowInner(showInput);
+ mWindowWasVisible = true;
+ mInShowWindow = false;
}
void showWindowInner(boolean showInput) {
@@ -1825,9 +1819,8 @@
initialize();
updateFullscreenMode();
updateInputViewShown();
-
- if (!mWindowAdded || !mWindowCreated) {
- mWindowAdded = true;
+
+ if (!mWindowCreated) {
mWindowCreated = true;
initialize();
if (DEBUG) Log.v(TAG, "CALL: onCreateCandidatesView");
@@ -1855,7 +1848,7 @@
final int nextImeWindowStatus = mapToImeWindowStatus(isInputViewShown());
if (previousImeWindowStatus != nextImeWindowStatus) {
- mImm.setImeWindowStatus(mToken, nextImeWindowStatus, mBackDisposition);
+ setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
}
if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
if (DEBUG) Log.v(TAG, "showWindow: showing!");
@@ -1880,7 +1873,7 @@
}
private void doHideWindow() {
- mImm.setImeWindowStatus(mToken, 0, mBackDisposition);
+ setImeWindowStatus(0, mBackDisposition);
hideWindow();
}
@@ -1921,11 +1914,24 @@
+ " mShouldClearInsetOfPreviousIme=" + mShouldClearInsetOfPreviousIme);
if (!mShouldClearInsetOfPreviousIme) return;
- mImm.clearLastInputMethodWindowForTransition(mToken);
+ clearLastInputMethodWindowForTransition();
mShouldClearInsetOfPreviousIme = false;
}
/**
+ * Tells the system that the IME decided to not show a window and the system no longer needs to
+ * use the previous IME's inset.
+ *
+ * <p>Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()}
+ * is the only expected caller of this method. Do not depend on this anywhere else.</p>
+ *
+ * <p>TODO: We probably need to reconsider how IME should be handled.</p>
+ */
+ private void clearLastInputMethodWindowForTransition() {
+ mPrivOps.clearLastInputMethodWindowForTransition();
+ }
+
+ /**
* Called when a new client has bound to the input method. This
* may be followed by a series of {@link #onStartInput(EditorInfo, boolean)}
* and {@link #onFinishInput()} calls as the user navigates through its
@@ -2132,7 +2138,7 @@
* @param flags Provides additional operating flags.
*/
public void requestHideSelf(int flags) {
- mImm.hideSoftInputFromInputMethodInternal(mToken, flags);
+ mPrivOps.hideMySoftInput(flags);
}
/**
@@ -2144,7 +2150,7 @@
* @param flags Provides additional operating flags.
*/
public final void requestShowSelf(int flags) {
- mImm.showSoftInputFromInputMethodInternal(mToken, flags);
+ mPrivOps.showMySoftInput(flags);
}
private boolean handleBack(boolean doIt) {
@@ -2838,7 +2844,32 @@
if (getCurrentInputConnection() != inputConnection) {
return;
}
- mImm.exposeContent(mToken, inputContentInfo, getCurrentInputEditorInfo());
+ exposeContentInternal(inputContentInfo, getCurrentInputEditorInfo());
+ }
+
+ /**
+ * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
+ * permission to the content.
+ *
+ * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
+ * InputConnection)} for details.</p>
+ *
+ * @param inputContentInfo Content to be temporarily exposed from the input method to the
+ * application.
+ * This cannot be {@code null}.
+ * @param editorInfo The editor that receives {@link InputContentInfo}.
+ */
+ private void exposeContentInternal(@NonNull InputContentInfo inputContentInfo,
+ @NonNull EditorInfo editorInfo) {
+ final Uri contentUri = inputContentInfo.getContentUri();
+ final IInputContentUriToken uriToken =
+ mPrivOps.createInputContentUriToken(contentUri, editorInfo.packageName);
+ if (uriToken == null) {
+ Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
+ + " packageName=" + editorInfo.packageName);
+ return;
+ }
+ inputContentInfo.setUriToken(uriToken);
}
private static int mapToImeWindowStatus(boolean isInputViewShown) {
@@ -2852,8 +2883,7 @@
@Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
final Printer p = new PrintWriterPrinter(fout);
p.println("Input method service state for " + this + ":");
- p.println(" mWindowCreated=" + mWindowCreated
- + " mWindowAdded=" + mWindowAdded);
+ p.println(" mWindowCreated=" + mWindowCreated);
p.println(" mWindowVisible=" + mWindowVisible
+ " mWindowWasVisible=" + mWindowWasVisible
+ " mInShowWindow=" + mInShowWindow);
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 795117e..b4b8887 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -16,15 +16,22 @@
package android.inputmethodservice;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Debug;
import android.os.IBinder;
+import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.WindowManager;
+import java.lang.annotation.Retention;
+
/**
* A SoftInputWindow is a Dialog that is intended to be used for a top-level input
* method window. It will be displayed along the edge of the screen, moving
@@ -33,6 +40,9 @@
* @hide
*/
public class SoftInputWindow extends Dialog {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "SoftInputWindow";
+
final String mName;
final Callback mCallback;
final KeyEvent.Callback mKeyEventCallback;
@@ -42,16 +52,65 @@
final boolean mTakesFocus;
private final Rect mBounds = new Rect();
+ @Retention(SOURCE)
+ @IntDef(value = {SoftInputWindowState.TOKEN_PENDING, SoftInputWindowState.TOKEN_SET,
+ SoftInputWindowState.SHOWN_AT_LEAST_ONCE, SoftInputWindowState.REJECTED_AT_LEAST_ONCE})
+ private @interface SoftInputWindowState {
+ /**
+ * The window token is not set yet.
+ */
+ int TOKEN_PENDING = 0;
+ /**
+ * The window token was set, but the window is not shown yet.
+ */
+ int TOKEN_SET = 1;
+ /**
+ * The window was shown at least once.
+ */
+ int SHOWN_AT_LEAST_ONCE = 2;
+ /**
+ * {@link android.view.WindowManager.BadTokenException} was sent when calling
+ * {@link Dialog#show()} at least once.
+ */
+ int REJECTED_AT_LEAST_ONCE = 3;
+ /**
+ * The window is considered destroyed. Any incoming request should be ignored.
+ */
+ int DESTROYED = 4;
+ }
+
+ @SoftInputWindowState
+ private int mWindowState = SoftInputWindowState.TOKEN_PENDING;
+
public interface Callback {
public void onBackPressed();
}
public void setToken(IBinder token) {
- WindowManager.LayoutParams lp = getWindow().getAttributes();
- lp.token = token;
- getWindow().setAttributes(lp);
+ switch (mWindowState) {
+ case SoftInputWindowState.TOKEN_PENDING:
+ // Normal scenario. Nothing to worry about.
+ WindowManager.LayoutParams lp = getWindow().getAttributes();
+ lp.token = token;
+ getWindow().setAttributes(lp);
+ updateWindowState(SoftInputWindowState.TOKEN_SET);
+ return;
+ case SoftInputWindowState.TOKEN_SET:
+ case SoftInputWindowState.SHOWN_AT_LEAST_ONCE:
+ case SoftInputWindowState.REJECTED_AT_LEAST_ONCE:
+ throw new IllegalStateException("setToken can be called only once");
+ case SoftInputWindowState.DESTROYED:
+ // Just ignore. Since there are multiple event queues from the token is issued
+ // in the system server to the timing when it arrives here, it can be delivered
+ // after the is already destroyed. No one should be blamed because of such an
+ // unfortunate but possible scenario.
+ Log.i(TAG, "Ignoring setToken() because window is already destroyed.");
+ return;
+ default:
+ throw new IllegalStateException("Unexpected state=" + mWindowState);
+ }
}
-
+
/**
* Create a SoftInputWindow that uses a custom style.
*
@@ -190,4 +249,109 @@
getWindow().setFlags(windowSetFlags, windowModFlags);
}
+
+ @Override
+ public final void show() {
+ switch (mWindowState) {
+ case SoftInputWindowState.TOKEN_PENDING:
+ throw new IllegalStateException("Window token is not set yet.");
+ case SoftInputWindowState.TOKEN_SET:
+ case SoftInputWindowState.SHOWN_AT_LEAST_ONCE:
+ // Normal scenario. Nothing to worry about.
+ try {
+ super.show();
+ updateWindowState(SoftInputWindowState.SHOWN_AT_LEAST_ONCE);
+ } catch (WindowManager.BadTokenException e) {
+ // Just ignore this exception. Since show() can be requested from other
+ // components such as the system and there could be multiple event queues before
+ // the request finally arrives here, the system may have already invalidated the
+ // window token attached to our window. In such a scenario, receiving
+ // BadTokenException here is an expected behavior. We just ignore it and update
+ // the state so that we do not touch this window later.
+ Log.i(TAG, "Probably the IME window token is already invalidated."
+ + " show() does nothing.");
+ updateWindowState(SoftInputWindowState.REJECTED_AT_LEAST_ONCE);
+ }
+ return;
+ case SoftInputWindowState.REJECTED_AT_LEAST_ONCE:
+ // Just ignore. In general we cannot completely avoid this kind of race condition.
+ Log.i(TAG, "Not trying to call show() because it was already rejected once.");
+ return;
+ case SoftInputWindowState.DESTROYED:
+ // Just ignore. In general we cannot completely avoid this kind of race condition.
+ Log.i(TAG, "Ignoring show() because the window is already destroyed.");
+ return;
+ default:
+ throw new IllegalStateException("Unexpected state=" + mWindowState);
+ }
+ }
+
+ final void dismissForDestroyIfNecessary() {
+ switch (mWindowState) {
+ case SoftInputWindowState.TOKEN_PENDING:
+ case SoftInputWindowState.TOKEN_SET:
+ // nothing to do because the window has never been shown.
+ updateWindowState(SoftInputWindowState.DESTROYED);
+ return;
+ case SoftInputWindowState.SHOWN_AT_LEAST_ONCE:
+ // Disable exit animation for the current IME window
+ // to avoid the race condition between the exit and enter animations
+ // when the current IME is being switched to another one.
+ try {
+ getWindow().setWindowAnimations(0);
+ dismiss();
+ } catch (WindowManager.BadTokenException e) {
+ // Just ignore this exception. Since show() can be requested from other
+ // components such as the system and there could be multiple event queues before
+ // the request finally arrives here, the system may have already invalidated the
+ // window token attached to our window. In such a scenario, receiving
+ // BadTokenException here is an expected behavior. We just ignore it and update
+ // the state so that we do not touch this window later.
+ Log.i(TAG, "Probably the IME window token is already invalidated. "
+ + "No need to dismiss it.");
+ }
+ // Either way, consider that the window is destroyed.
+ updateWindowState(SoftInputWindowState.DESTROYED);
+ return;
+ case SoftInputWindowState.REJECTED_AT_LEAST_ONCE:
+ // Just ignore. In general we cannot completely avoid this kind of race condition.
+ Log.i(TAG,
+ "Not trying to dismiss the window because it is most likely unnecessary.");
+ // Anyway, consider that the window is destroyed.
+ updateWindowState(SoftInputWindowState.DESTROYED);
+ return;
+ case SoftInputWindowState.DESTROYED:
+ throw new IllegalStateException(
+ "dismissForDestroyIfNecessary can be called only once");
+ default:
+ throw new IllegalStateException("Unexpected state=" + mWindowState);
+ }
+ }
+
+ private void updateWindowState(@SoftInputWindowState int newState) {
+ if (DEBUG) {
+ if (mWindowState != newState) {
+ Log.d(TAG, "WindowState: " + stateToString(mWindowState) + " -> "
+ + stateToString(newState) + " @ " + Debug.getCaller());
+ }
+ }
+ mWindowState = newState;
+ }
+
+ private static String stateToString(@SoftInputWindowState int state) {
+ switch (state) {
+ case SoftInputWindowState.TOKEN_PENDING:
+ return "TOKEN_PENDING";
+ case SoftInputWindowState.TOKEN_SET:
+ return "TOKEN_SET";
+ case SoftInputWindowState.SHOWN_AT_LEAST_ONCE:
+ return "SHOWN_AT_LEAST_ONCE";
+ case SoftInputWindowState.REJECTED_AT_LEAST_ONCE:
+ return "REJECTED_AT_LEAST_ONCE";
+ case SoftInputWindowState.DESTROYED:
+ return "DESTROYED";
+ default:
+ throw new IllegalStateException("Unknown state=" + state);
+ }
+ }
}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index f17e0f0..ded3a19 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.os.LooperProto;
import android.util.Log;
import android.util.Printer;
import android.util.Slog;
@@ -70,6 +69,7 @@
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
+ private static Observer sObserver;
final MessageQueue mQueue;
final Thread mThread;
@@ -131,6 +131,15 @@
}
/**
+ * Set the transaction observer for all Loopers in this process.
+ *
+ * @hide
+ */
+ public static void setObserver(@Nullable Observer observer) {
+ sObserver = observer;
+ }
+
+ /**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
@@ -169,6 +178,8 @@
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
+ // Make sure the observer won't change while processing a transaction.
+ final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
@@ -189,9 +200,21 @@
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
+ Object token = null;
+ if (observer != null) {
+ token = observer.messageDispatchStarting();
+ }
try {
msg.target.dispatchMessage(msg);
+ if (observer != null) {
+ observer.messageDispatched(token, msg);
+ }
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
+ } catch (Exception exception) {
+ if (observer != null) {
+ observer.dispatchingThrewException(token, msg, exception);
+ }
+ throw exception;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
@@ -386,7 +409,9 @@
final long looperToken = proto.start(fieldId);
proto.write(LooperProto.THREAD_NAME, mThread.getName());
proto.write(LooperProto.THREAD_ID, mThread.getId());
- mQueue.writeToProto(proto, LooperProto.QUEUE);
+ if (mQueue != null) {
+ mQueue.writeToProto(proto, LooperProto.QUEUE);
+ }
proto.end(looperToken);
}
@@ -395,4 +420,39 @@
return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+ ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
+
+ /** {@hide} */
+ public interface Observer {
+ /**
+ * Called right before a message is dispatched.
+ *
+ * <p> The token type is not specified to allow the implementation to specify its own type.
+ *
+ * @return a token used for collecting telemetry when dispatching a single message.
+ * The token token must be passed back exactly once to either
+ * {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException}
+ * and must not be reused again.
+ *
+ */
+ Object messageDispatchStarting();
+
+ /**
+ * Called when a message was processed by a Handler.
+ *
+ * @param token Token obtained by previously calling
+ * {@link Observer#messageDispatchStarting} on the same Observer instance.
+ * @param msg The message that was dispatched.
+ */
+ void messageDispatched(Object token, Message msg);
+
+ /**
+ * Called when an exception was thrown while processing a message.
+ *
+ * @param token Token obtained by previously calling
+ * {@link Observer#messageDispatchStarting} on the same Observer instance.
+ * @param msg The message that was dispatched and caused an exception.
+ * @param exception The exception that was thrown.
+ */
+ void dispatchingThrewException(Object token, Message msg, Exception exception);
+ }
}
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 7b3ea57..b13bcac 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -127,6 +127,15 @@
}
/**
+ * Adds a boolean by adding either a 1 or 0 to the output.
+ */
+ public void writeBoolean(boolean val) {
+ int toWrite = val ? 1 : 0;
+ mStorage.write(EVENT_TYPE_INT);
+ write4Bytes(toWrite);
+ }
+
+ /**
* Writes the stored fields to a byte array. Will first write a new-line character to denote
* END_LIST before writing contents to byte array.
*/
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index e8daf21..a871425 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -104,7 +104,7 @@
@UnsupportedAppUsage
public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) {
- this(context, streamType, defaultUri, callback, true /* playSample */);
+ this(context, streamType, defaultUri, callback, false /* playSample */);
}
public SeekBarVolumizer(
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a1cc67d..e007398 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7256,6 +7256,14 @@
private static final Validator DOZE_REACH_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
/**
+ * Gesture that wakes up the display, showing the ambient version of the status bar.
+ * @hide
+ */
+ public static final String DOZE_WAKE_SCREEN_GESTURE = "doze_wake_screen_gesture";
+
+ private static final Validator DOZE_WAKE_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+ /**
* The current night mode that has been selected by the user. Owned
* and controlled by UiModeManagerService. Constants are as per
* UiModeManager.
@@ -8158,6 +8166,7 @@
DOZE_PICK_UP_GESTURE,
DOZE_DOUBLE_TAP_GESTURE,
DOZE_REACH_GESTURE,
+ DOZE_WAKE_SCREEN_GESTURE,
NFC_PAYMENT_DEFAULT_COMPONENT,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
FACE_UNLOCK_KEYGUARD_ENABLED,
@@ -8302,6 +8311,7 @@
VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR);
VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR);
VALIDATORS.put(DOZE_REACH_GESTURE, DOZE_REACH_GESTURE_VALIDATOR);
+ VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR);
VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
@@ -13308,6 +13318,36 @@
* @hide
*/
public static final String BINDER_CALLS_STATS = "binder_calls_stats";
+
+ /**
+ * Looper stats settings.
+ *
+ * The following strings are supported as keys:
+ * <pre>
+ * enabled (boolean)
+ * sampling_interval (int)
+ * </pre>
+ *
+ * @hide
+ */
+ public static final String LOOPER_STATS = "looper_stats";
+
+ /**
+ * Default user id to boot into. They map to user ids, for example, 10, 11, 12.
+ *
+ * @hide
+ */
+ public static final String DEFAULT_USER_ID_TO_BOOT_INTO = "default_boot_into_user_id";
+
+ /**
+ * Persistent user id that is last logged in to.
+ *
+ * They map to user ids, for example, 10, 11, 12.
+ *
+ * @hide
+ */
+ public static final String LAST_ACTIVE_USER_ID = "last_active_persistent_user_id";
+
}
/**
diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index f52ac85..fa6bd65 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -78,7 +78,7 @@
int childViewId) throws Exception {
final StringBuilder converted = new StringBuilder();
final int size = mFields.size();
- if (sDebug) Log.d(TAG, size + " multiple fields on id " + childViewId);
+ if (sDebug) Log.d(TAG, size + " fields on id " + childViewId);
for (Entry<AutofillId, Pair<Pattern, String>> entry : mFields.entrySet()) {
final AutofillId id = entry.getKey();
final Pair<Pattern, String> field = entry.getValue();
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 3b820ca..26240c5 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -52,6 +52,9 @@
* {@link #onNotificationEnqueued(StatusBarNotification)} will only be called for notifications
* sent to the current user, and {@link Adjustment adjuments} will only be accepted for the
* current user.
+ * <p>
+ * All callbacks are called on the main thread.
+ * </p>
*
* @hide
*/
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 1b588f4..d9ed2aa 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -91,6 +91,10 @@
* notification listeners running in a work profile. A
* {@link android.app.admin.DevicePolicyManager} might block notifications originating from a work
* profile.</p>
+ * <p>
+ * From {@link Build.VERSION_CODES#N} onward all callbacks are called on the main thread. Prior
+ * to N, there is no guarantee on what thread the callback will happen.
+ * </p>
*/
public abstract class NotificationListenerService extends Service {
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index dd97d52..84826e0 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -18,7 +18,7 @@
import android.annotation.UnsupportedAppUsage;
import android.app.Notification;
-import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -261,7 +261,7 @@
return this.user.getIdentifier();
}
- /** The package of the app that posted the notification. */
+ /** The package that the notification belongs to. */
public String getPackageName() {
return pkg;
}
@@ -277,14 +277,18 @@
return tag;
}
- /** The notifying app's calling uid. @hide */
- @UnsupportedAppUsage
+ /**
+ * The notifying app's ({@link #getPackageName()}'s) uid.
+ */
public int getUid() {
return uid;
}
- /** The package used for AppOps tracking. @hide */
- @UnsupportedAppUsage
+ /** The package that posted the notification.
+ *<p>
+ * Might be different from {@link #getPackageName()} if the app owning the notification has
+ * a {@link NotificationManager#setNotificationDelegate(String) notification delegate}.
+ */
public String getOpPkg() {
return opPkg;
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index febdb83..4bd86a4 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -164,8 +164,7 @@
int mType;
int mCurWidth;
int mCurHeight;
- int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
- | WindowManager.LayoutParams.FLAG_SCALED;
+ int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
int mWindowPrivateFlags =
WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
int mCurWindowFlags = mWindowFlags;
@@ -776,6 +775,7 @@
mDisplay.getDisplayInfo(displayInfo);
mLayout.width = Math.max(displayInfo.logicalWidth, myWidth);
mLayout.height = Math.max(displayInfo.logicalHeight, myHeight);
+ mWindowFlags |= WindowManager.LayoutParams.FLAG_SCALED;
}
mLayout.format = mFormat;
@@ -785,8 +785,7 @@
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_SCALED;
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mCurWindowPrivateFlags = mWindowPrivateFlags;
mLayout.privateFlags = mWindowPrivateFlags;
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 9e0fee33..19c55d5 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -181,7 +181,8 @@
public static final class Family {
private final @NonNull String mName;
private final @NonNull Font[] mFonts;
- private final @NonNull String[] mLanguages;
+ // Comma separated BCP47 complient locale strings
+ private final @NonNull String mLanguages;
/** @hide */
@Retention(SOURCE)
@@ -219,7 +220,7 @@
// See frameworks/minikin/include/minikin/FontFamily.h
private final @Variant int mVariant;
- public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String[] languages,
+ public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String languages,
@Variant int variant) {
mName = name;
mFonts = fonts;
@@ -244,9 +245,9 @@
}
/**
- * Returns the languages for this family. May be null.
+ * Returns the comma separated BCP47 complient languages for this family. May be null.
*/
- public @Nullable String[] getLanguages() {
+ public @NonNull String getLanguages() {
return mLanguages;
}
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 4f1488e..e4200ac 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -16,15 +16,145 @@
package android.text;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
- * Hyphenator just initializes the native implementation of automatic hyphenation,
- * in essence finding valid hyphenation opportunities in a word.
+ * Provides constants and pack/unpack methods for the HyphenEdit.
*
- * @hide
+ * Hyphenator provides constant values for start of line and end of line modification.
+ * For example, by passing {@link #END_HYPHEN_EDIT_INSERT_HYPHEN} like as follows, HYPHEN(U+2010)
+ * character is appended at the end of line.
+ *
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * paint.setHyphenEdit(Hyphenator.packHyphenEdit(
+ * Hyphenator.START_HYPHEN_EDIT_NO_EDIT,
+ * Hyphenator.END_HYPHEN_EDIT_INSERT_HYPHEN));
+ * paint.measureText("abc", 0, 3); // Returns the width of "abc‐"
+ * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "abc‐"
+ * </code>
+ * </pre>
+ *
+ * @see android.graphics.Paint#setHyphenEdit(int)
*/
public class Hyphenator {
+ private Hyphenator() {}
+
+ /** @hide */
+ @IntDef(prefix = { "START_HYPHEN_EDIT_" }, value = {
+ START_HYPHEN_EDIT_NO_EDIT,
+ START_HYPHEN_EDIT_INSERT_HYPHEN,
+ START_HYPHEN_EDIT_INSERT_ZWJ
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StartHyphenEdit {}
+
+ /**
+ * An integer representing the starting of the line has no modification for hyphenation.
+ */
+ public static final int START_HYPHEN_EDIT_NO_EDIT = 0x00;
+
+ /**
+ * An integer representing the starting of the line has normal hyphen character (U+002D).
+ */
+ public static final int START_HYPHEN_EDIT_INSERT_HYPHEN = 0x01;
+
+ /**
+ * An integer representing the starting of the line has Zero-Width-Joiner (U+200D).
+ */
+ public static final int START_HYPHEN_EDIT_INSERT_ZWJ = 0x02;
+
+ /** @hide */
+ @IntDef(prefix = { "END_HYPHEN_EDIT_" }, value = {
+ END_HYPHEN_EDIT_NO_EDIT,
+ END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN,
+ END_HYPHEN_EDIT_INSERT_HYPHEN,
+ END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN,
+ END_HYPHEN_EDIT_INSERT_MAQAF,
+ END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN,
+ END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EndHyphenEdit {}
+
+ /**
+ * An integer representing the end of the line has no modification for hyphenation.
+ */
+ public static final int END_HYPHEN_EDIT_NO_EDIT = 0x00;
+
+ /**
+ * An integer representing the character at the end of the line is replaced with hyphen
+ * character (U+002D).
+ */
+ public static final int END_HYPHEN_EDIT_REPLACE_WITH_HYPHEN = 0x01;
+
+ /**
+ * An integer representing the end of the line has normal hyphen character (U+002D).
+ */
+ public static final int END_HYPHEN_EDIT_INSERT_HYPHEN = 0x02;
+
+ /**
+ * An integer representing the end of the line has Armentian hyphen (U+058A).
+ */
+ public static final int END_HYPHEN_EDIT_INSERT_ARMENIAN_HYPHEN = 0x03;
+
+ /**
+ * An integer representing the end of the line has maqaf (Hebrew hyphen, U+05BE).
+ */
+ public static final int END_HYPHEN_EDIT_INSERT_MAQAF = 0x04;
+
+ /**
+ * An integer representing the end of the line has Canadian Syllabics hyphen (U+1400).
+ */
+ public static final int END_HYPHEN_EDIT_INSERT_UCAS_HYPHEN = 0x05;
+
+ /**
+ * An integer representing the end of the line has Zero-Width-Joiner (U+200D) followed by normal
+ * hyphen character (U+002D).
+ */
+ public static final int END_HYPHEN_EDIT_INSERT_ZWJ_AND_HYPHEN = 0x06;
+
+ // Following three constants are used for packing start hyphen edit and end hyphen edit into
+ // single integer. Following encodings must be the same as the minikin's one.
+ // See frameworks/minikin/include/Hyphenator.h for more details.
+ private static final int END_HYPHEN_EDIT_MASK = 0x07; // 0b00111
+ private static final int START_HYPHEN_EDIT_MASK = 0x18; // 0b11000
+ private static final int START_HYPHEN_EDIT_SHIFT = 0x03;
+
+ /**
+ * Extract start hyphen edit from packed value.
+ */
+ public static @StartHyphenEdit int unpackStartHyphenEdit(int hyphenEdit) {
+ return (hyphenEdit & START_HYPHEN_EDIT_MASK) >> START_HYPHEN_EDIT_SHIFT;
+ }
+
+ /**
+ * Extract end hyphen edit from packed value.
+ */
+ public static @EndHyphenEdit int unpackEndHyphenEdit(int hyphenEdit) {
+ return hyphenEdit & END_HYPHEN_EDIT_MASK;
+ }
+
+ /**
+ * Pack the start hyphen edit and end hyphen edit into single integer.
+ */
+ public static int packHyphenEdit(@StartHyphenEdit int startHyphenEdit,
+ @EndHyphenEdit int endHyphenEdit) {
+ return ((startHyphenEdit << START_HYPHEN_EDIT_SHIFT) & START_HYPHEN_EDIT_MASK)
+ | (endHyphenEdit & END_HYPHEN_EDIT_MASK);
+ }
+
+
+ /**
+ * @hide
+ */
public static void init() {
nInit();
}
+
private static native void nInit();
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 128f860..5adb1ca 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1216,11 +1216,19 @@
}
/**
+ * Returns the packed hyphen edit value for this line.
+ *
+ * You can extract start hyphen edit and end hyphen edit by using
+ * {@link Hyphenator#unpackStartHyphenEdit(int)} and
+ * {@link Hyphenator#unpackEndHyphenEdit(int)}.
+ *
+ * @param lineNumber a line number
+ * @return A packed hyphen edit value.
* @hide
*/
@Override
- public int getHyphen(int line) {
- return mLines[mColumns * line + HYPHEN] & HYPHEN_MASK;
+ public int getHyphen(int lineNumber) {
+ return mLines[mColumns * lineNumber + HYPHEN] & HYPHEN_MASK;
}
/**
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 9667b106..bf2d600 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -1053,13 +1053,16 @@
return runIsRtl ? -ret : ret;
}
- private int adjustHyphenEdit(int start, int limit, int hyphenEdit) {
- int result = hyphenEdit;
+ private int adjustHyphenEdit(int start, int limit, int packedHyphenEdit) {
+ int result = packedHyphenEdit;
// Only draw hyphens on first or last run in line. Disable them otherwise.
if (start > 0) { // not the first run
- result &= ~Paint.HYPHENEDIT_MASK_START_OF_LINE;
+ result = Hyphenator.packHyphenEdit(Hyphenator.START_HYPHEN_EDIT_NO_EDIT,
+ Hyphenator.unpackEndHyphenEdit(packedHyphenEdit));
}
if (limit < mLen) { // not the last run
+ result = Hyphenator.packHyphenEdit(Hyphenator.unpackStartHyphenEdit(packedHyphenEdit),
+ Hyphenator.END_HYPHEN_EDIT_NO_EDIT);
result &= ~Paint.HYPHENEDIT_MASK_END_OF_LINE;
}
return result;
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index 50ee5f3..2742ae0 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -16,11 +16,16 @@
package android.text.style;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Px;
import android.graphics.Paint;
import android.text.TextPaint;
+import com.android.internal.util.Preconditions;
+
/**
- * The classes that affect the height of the line should implement this interface.
+ * The classes that affect the line height of paragraph should implement this interface.
*/
public interface LineHeightSpan extends ParagraphStyle, WrapTogetherSpan {
/**
@@ -38,8 +43,8 @@
Paint.FontMetricsInt fm);
/**
- * The classes that affect the height of the line with respect to density, should implement this
- * interface.
+ * The classes that affect the line height of paragraph with respect to density,
+ * should implement this interface.
*/
public interface WithDensity extends LineHeightSpan {
@@ -57,4 +62,38 @@
int spanstartv, int lineHeight,
Paint.FontMetricsInt fm, TextPaint paint);
}
+
+ /**
+ * Default implementation of the {@link LineHeightSpan}, which changes the line height of the
+ * attached paragraph.
+ * <p>
+ * LineHeightSpan will change the line height of the entire paragraph, even though it
+ * covers only part of the paragraph.
+ * </p>
+ */
+ class Standard implements LineHeightSpan {
+
+ private final @Px int mHeight;
+ /**
+ * Set the line height of the paragraph to <code>height</code> physical pixels.
+ */
+ public Standard(@Px @IntRange(from = 1) int height) {
+ Preconditions.checkArgument(height > 0, "Height:" + height + "must be positive");
+ mHeight = height;
+ }
+
+ @Override
+ public void chooseHeight(@NonNull CharSequence text, int start, int end,
+ int spanstartv, int lineHeight,
+ @NonNull Paint.FontMetricsInt fm) {
+ final int originHeight = fm.descent - fm.ascent;
+ // If original height is not positive, do nothing.
+ if (originHeight <= 0) {
+ return;
+ }
+ final float ratio = mHeight * 1.0f / originHeight;
+ fm.descent = Math.round(fm.descent * ratio);
+ fm.ascent = fm.descent - mHeight;
+ }
+ }
}
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index 38b89dc..41d80fc 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -94,7 +94,7 @@
* @param fadingMode The behavior of this transition, a combination of
* {@link #IN} and {@link #OUT}.
*/
- public Fade(int fadingMode) {
+ public Fade(@VisibilityMode int fadingMode) {
setMode(fadingMode);
}
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 9cf3210..14a513b 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -147,7 +147,7 @@
/**
* Constructor using the provided slide edge direction.
*/
- public Slide(int slideEdge) {
+ public Slide(@GravityFlag int slideEdge) {
setSlideEdge(slideEdge);
}
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 77c652e..319f080 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -52,9 +52,11 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "MODE_" }, value = {
+ @IntDef(flag = true, value = {
MODE_IN,
- MODE_OUT
+ MODE_OUT,
+ Fade.IN,
+ Fade.OUT
})
@interface VisibilityMode {}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index a7f14b6..b5ade2a 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -45,6 +45,7 @@
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put("settings_dynamic_homepage", "false");
DEFAULT_FLAGS.put("settings_mobile_network_v2", "false");
+ DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true");
}
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 2c9bb66..ea4464d 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -232,6 +232,18 @@
};
/**
+ * Determine if a value is a color.
+ *
+ * This works by comparing {@link #type} to {@link #TYPE_FIRST_COLOR_INT}
+ * and {@link #TYPE_LAST_COLOR_INT}.
+ *
+ * @return true if this value is a color
+ */
+ public boolean isColorType() {
+ return (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT);
+ }
+
+ /**
* Retrieve the base value from a complex data integer. This uses the
* {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of
* the data to compute a floating point representation of the number they
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c205af0..bfe1e95 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -391,15 +391,16 @@
void registerShortcutKey(in long shortcutCode, IShortcutService keySubscriber);
/**
- * Create an input consumer by name.
+ * Create an input consumer by name and display id.
*/
- void createInputConsumer(IBinder token, String name, out InputChannel inputChannel);
+ void createInputConsumer(IBinder token, String name, int displayId,
+ out InputChannel inputChannel);
/**
- * Destroy an input consumer by name. This method will also dispose the input channels
- * associated with that InputConsumer.
+ * Destroy an input consumer by name and display id.
+ * This method will also dispose the input channels associated with that InputConsumer.
*/
- boolean destroyInputConsumer(String name);
+ boolean destroyInputConsumer(String name, int displayId);
/**
* Return the touch region for the current IME window, or an empty region if there is none.
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 274dd2f..514a11e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -545,7 +545,8 @@
mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
if (creating) {
- mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
+ viewRoot.createBoundsSurface(mSubLayer);
+ mSurfaceSession = new SurfaceSession(viewRoot.mBoundsSurface);
mDeferredDestroySurfaceControl = mSurfaceControl;
updateOpaqueFlag();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9bc53ed..4a61906 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8251,7 +8251,7 @@
if (mAutofillId == null) {
// The autofill id needs to be unique, but its value doesn't matter,
// so it's better to reuse the accessibility id to save space.
- mAutofillId = new AutofillId(getAutofillViewId());
+ mAutofillId = new AutofillId(getAutofillManager(), getAutofillViewId());
}
return mAutofillId;
}
@@ -8313,11 +8313,15 @@
// Ignore reset because it was never explicitly set before.
return;
}
- mAutofillId = id;
if (id != null) {
+ // Must create a new id so the session id is preserved.
+ final int oldSessionId = mAutofillId.getSessionId();
mAutofillViewId = id.getViewId();
+ mAutofillId = new AutofillId(mAutofillViewId);
+ mAutofillId.setSessionId(oldSessionId);
mPrivateFlags3 |= PFLAG3_AUTOFILLID_EXPLICITLY_SET;
} else {
+ mAutofillId = null;
mAutofillViewId = NO_ID;
mPrivateFlags3 &= ~PFLAG3_AUTOFILLID_EXPLICITLY_SET;
}
@@ -8607,8 +8611,11 @@
structure.setContextClickable(true);
}
if (forAutofill) {
- structure.setAutofillId(new AutofillId(getAutofillId(),
- AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId())));
+ final AutofillId autofillId = new AutofillId(getAutofillId(),
+ AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()));
+ final AutofillManager afm = getAutofillManager();
+ autofillId.setSessionId(afm == null ? AutofillManager.NO_SESSION : afm.getSessionId());
+ structure.setAutofillId(autofillId);
}
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1351f6f1..16d202b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -81,6 +81,7 @@
import android.util.TimeUtils;
import android.util.TypedValue;
import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl.Transaction;
import android.view.ThreadedRenderer.FrameDrawingCallback;
import android.view.View.AttachInfo;
import android.view.View.FocusDirection;
@@ -342,6 +343,7 @@
final Rect mTempRect; // used in the transaction to not thrash the heap.
final Rect mVisRect; // used to retrieve visible rect of focused view.
+ private final Rect mTempBoundsRect = new Rect(); // used to set the size of the bounds surface.
// This is used to reduce the race between window focus changes being dispatched from
// the window manager and input events coming through the input system.
@@ -404,6 +406,18 @@
@UnsupportedAppUsage
public final Surface mSurface = new Surface();
+ /**
+ * Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds
+ * are set to the parent's bounds adjusted for surface insets. This surface is created when
+ * {@link ViewRootImpl#createBoundsSurface(int)} is called.
+ * By parenting to this bounds surface, child surfaces can ensure they do not draw into the
+ * surface inset regions set by the parent window.
+ */
+ public final Surface mBoundsSurface = new Surface();
+ private SurfaceSession mSurfaceSession;
+ private SurfaceControl mBoundsSurfaceControl;
+ private final Transaction mTransaction = new Transaction();
+
@UnsupportedAppUsage
boolean mAdded;
boolean mAddedTouchMode;
@@ -1390,12 +1404,79 @@
}
if (mStopped) {
- mSurface.release();
+ destroySurface();
}
}
}
/**
+ * Creates a surface as a child of {@code mSurface} with the same bounds as its parent and
+ * crop bounds set to the parent's bounds adjusted for surface insets.
+ *
+ * @param zOrderLayer Z order relative to the parent surface.
+ */
+ public void createBoundsSurface(int zOrderLayer) {
+ if (mSurfaceSession == null) {
+ mSurfaceSession = new SurfaceSession(mSurface);
+ }
+ if (mBoundsSurfaceControl != null && mBoundsSurface.isValid()) {
+ return; // surface control for bounds surface already exists.
+ }
+
+ mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+ .setName("Bounds for - " + getTitle().toString())
+ .setSize(mWidth, mHeight)
+ .build();
+
+ setBoundsSurfaceSizeAndCrop();
+ mTransaction.setLayer(mBoundsSurfaceControl, zOrderLayer)
+ .show(mBoundsSurfaceControl)
+ .apply();
+ mBoundsSurface.copyFrom(mBoundsSurfaceControl);
+ }
+
+ private void setBoundsSurfaceSizeAndCrop() {
+ // 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(mBoundsSurfaceControl, mTempBoundsRect);
+
+ // Expand the bounds by the surface insets to get the size of surface.
+ mTempBoundsRect.inset(-mWindowAttributes.surfaceInsets.left,
+ -mWindowAttributes.surfaceInsets.top,
+ -mWindowAttributes.surfaceInsets.right,
+ -mWindowAttributes.surfaceInsets.bottom);
+ mTransaction.setSize(mBoundsSurfaceControl, mTempBoundsRect.width(),
+ mTempBoundsRect.height());
+ }
+
+ /**
+ * 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 updateBoundsSurface() {
+ if (mBoundsSurfaceControl != null && mSurface.isValid()) {
+ setBoundsSurfaceSizeAndCrop();
+ mTransaction.deferTransactionUntilSurface(mBoundsSurfaceControl,
+ mSurface, mSurface.getNextFrameNumber())
+ .apply();
+ }
+ }
+
+ private void destroySurface() {
+ mSurface.release();
+ mSurfaceSession = null;
+
+ if (mBoundsSurfaceControl != null) {
+ mBoundsSurfaceControl.destroy();
+ mBoundsSurface.release();
+ mBoundsSurfaceControl = null;
+ }
+ }
+
+ /**
* Block the input events during an Activity Transition. The KEYCODE_BACK event is allowed
* through to allow quick reversal of the Activity Transition.
*
@@ -2350,6 +2431,10 @@
maybeHandleWindowMove(frame);
}
+ if (surfaceChanged) {
+ updateBoundsSurface();
+ }
+
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
@@ -3860,7 +3945,7 @@
mView = null;
mAttachInfo.mRootView = null;
- mSurface.release();
+ destroySurface();
if (mInputQueueCallback != null && mInputQueue != null) {
mInputQueueCallback.onInputQueueDestroyed(mInputQueue);
@@ -6809,7 +6894,7 @@
}
}
- mSurface.release();
+ destroySurface();
}
}
diff --git a/core/java/android/view/accessibility/TEST_MAPPING b/core/java/android/view/accessibility/TEST_MAPPING
new file mode 100644
index 0000000..d2bd6ea
--- /dev/null
+++ b/core/java/android/view/accessibility/TEST_MAPPING
@@ -0,0 +1,53 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsAccessibilityServiceTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsUiAutomationTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.accessibility"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsAccessibilityServiceTestCases"
+ },
+ {
+ "name": "CtsUiAutomationTestCases"
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.accessibility"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index cb1d89c..5ae91a5 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -15,6 +15,10 @@
*/
package android.view.autofill;
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,6 +32,7 @@
private final int mViewId;
private final boolean mVirtual;
private final int mVirtualId;
+ private int mSessionId = AutofillManager.NO_SESSION;
/** @hide */
@TestApi
@@ -38,18 +43,26 @@
}
/** @hide */
+ // NOTE: caller must set sessionId
@TestApi
- public AutofillId(AutofillId parent, int virtualChildId) {
+ public AutofillId(@NonNull AutofillId parent, int virtualChildId) {
mVirtual = true;
mViewId = parent.mViewId;
mVirtualId = virtualChildId;
}
/** @hide */
- public AutofillId(int parentId, int virtualChildId) {
+ public AutofillId(int sessionId, int parentId, int virtualChildId) {
mVirtual = true;
mViewId = parentId;
mVirtualId = virtualChildId;
+ mSessionId = sessionId;
+ }
+
+ /** @hide */
+ public AutofillId(@Nullable AutofillManager afm, int id) {
+ this(id);
+ mSessionId = afm == null ? AutofillManager.NO_SESSION : afm.getSessionId();
}
/** @hide */
@@ -67,6 +80,16 @@
return mVirtual;
}
+ /** @hide */
+ public int getSessionId() {
+ return mSessionId;
+ }
+
+ /** @hide */
+ public void setSessionId(int sessionId) {
+ this.mSessionId = sessionId;
+ }
+
/////////////////////////////////
// Object "contract" methods. //
/////////////////////////////////
@@ -77,6 +100,7 @@
int result = 1;
result = prime * result + mViewId;
result = prime * result + mVirtualId;
+ result = prime * result + mSessionId;
return result;
}
@@ -88,6 +112,7 @@
final AutofillId other = (AutofillId) obj;
if (mViewId != other.mViewId) return false;
if (mVirtualId != other.mVirtualId) return false;
+ if (mSessionId != other.mSessionId) return false;
return true;
}
@@ -97,6 +122,9 @@
if (mVirtual) {
builder.append(':').append(mVirtualId);
}
+ if (mSessionId != AutofillManager.NO_SESSION && sDebug) {
+ builder.append('<').append(mSessionId).append('>');
+ }
return builder.toString();
}
@@ -110,12 +138,14 @@
parcel.writeInt(mViewId);
parcel.writeInt(mVirtual ? 1 : 0);
parcel.writeInt(mVirtualId);
+ parcel.writeInt(mSessionId);
}
private AutofillId(Parcel parcel) {
mViewId = parcel.readInt();
mVirtual = parcel.readInt() == 1;
mVirtualId = parcel.readInt();
+ mSessionId = parcel.readInt();
}
public static final Parcelable.Creator<AutofillId> CREATOR =
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9419e93..612888e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -916,7 +916,7 @@
boolean isVisible, boolean virtual) {
synchronized (mLock) {
if (mEnabled && isActiveLocked()) {
- final AutofillId id = virtual ? getAutofillId(view, virtualId)
+ final AutofillId id = virtual ? getAutofillIdLocked(view, virtualId)
: view.getAutofillId();
if (sVerbose) Log.v(TAG, "visibility changed for " + id + ": " + isVisible);
if (!isVisible && mFillableIds != null) {
@@ -976,7 +976,7 @@
@GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
int flags) {
- final AutofillId id = getAutofillId(view, virtualId);
+ final AutofillId id = getAutofillIdLocked(view, virtualId);
AutofillCallback callback = null;
if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
@@ -1033,7 +1033,7 @@
if (mEnabled && isActiveLocked()) {
// don't notify exited when Activity is already in background
if (!isClientDisablingEnterExitEvent()) {
- final AutofillId id = getAutofillId(view, virtualId);
+ final AutofillId id = getAutofillIdLocked(view, virtualId);
// Update focus on existing session.
updateSessionLocked(id, null, null, ACTION_VIEW_EXITED, 0);
@@ -1116,7 +1116,7 @@
return;
}
- final AutofillId id = getAutofillId(view, virtualId);
+ final AutofillId id = getAutofillIdLocked(view, virtualId);
updateSessionLocked(id, null, value, ACTION_VALUE_CHANGED, 0);
}
}
@@ -1137,7 +1137,11 @@
* @param virtualId id identifying the virtual child inside the parent view.
*/
public void notifyViewClicked(@NonNull View view, int virtualId) {
- notifyViewClicked(getAutofillId(view, virtualId));
+ final AutofillId id;
+ synchronized (mLock) {
+ id = getAutofillIdLocked(view, virtualId);
+ }
+ notifyViewClicked(id);
}
private void notifyViewClicked(AutofillId id) {
@@ -1534,8 +1538,9 @@
return id;
}
- private static AutofillId getAutofillId(View parent, int virtualId) {
- return new AutofillId(parent.getAutofillViewId(), virtualId);
+ @GuardedBy("mLock")
+ private AutofillId getAutofillIdLocked(View parent, int virtualId) {
+ return new AutofillId(mSessionId, parent.getAutofillViewId(), virtualId);
}
@GuardedBy("mLock")
@@ -1566,6 +1571,8 @@
mSessionId = receiver.getIntResult();
if (mSessionId != NO_SESSION) {
mState = STATE_ACTIVE;
+ // Need to update the view's autofill id with the session
+ id.setSessionId(mSessionId);
}
client.autofillClientResetableStateAvailable();
} catch (RemoteException e) {
@@ -2210,6 +2217,13 @@
return getStateAsString(mState);
}
+ /** @hide */
+ public int getSessionId() {
+ synchronized (mLock) {
+ return mSessionId;
+ }
+ }
+
@NonNull
private static String getStateAsString(int state) {
switch (state) {
diff --git a/core/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java
index 7104a28..0d0fea6 100644
--- a/core/java/android/view/inputmethod/InputContentInfo.java
+++ b/core/java/android/view/inputmethod/InputContentInfo.java
@@ -183,7 +183,15 @@
@Nullable
public Uri getLinkUri() { return mLinkUri; }
- void setUriToken(IInputContentUriToken token) {
+ /**
+ * Update the internal state of this object to be associated with the given token.
+ *
+ * <p>TODO(yukawa): Come up with an idea to make {@link InputContentInfo} immutable.</p>
+ *
+ * @param token special URI token obtained from the system.
+ * @hide
+ */
+ public void setUriToken(IInputContentUriToken token) {
if (mUriToken != null) {
throw new IllegalStateException("URI token is already set");
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index ab8886b..f45507c 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -25,6 +25,8 @@
import android.os.IBinder;
import android.os.ResultReceiver;
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+
/**
* The InputMethod interface represents an input method which can generate key
* events and text, such as digital, email addresses, CJK characters, other
@@ -79,17 +81,35 @@
public interface SessionCallback {
public void sessionCreated(InputMethodSession session);
}
-
+
+ /**
+ * Called first thing after an input method is created, this supplies a
+ * unique token for the session it has with the system service as well as
+ * IPC endpoint to do some other privileged operations.
+ *
+ * @param token special token for the system to identify
+ * {@link InputMethodService}
+ * @param privilegedOperations IPC endpoint to do some privileged
+ * operations that are allowed only to the
+ * current IME.
+ * @hide
+ */
+ @MainThread
+ default void initializeInternal(IBinder token,
+ IInputMethodPrivilegedOperations privilegedOperations) {
+ attachToken(token);
+ }
+
/**
* Called first thing after an input method is created, this supplies a
* unique token for the session it has with the system service. It is
* needed to identify itself with the service to validate its operations.
* This token <strong>must not</strong> be passed to applications, since
* it grants special priviledges that should not be given to applications.
- *
- * <p>Note: to protect yourself from malicious clients, you should only
- * accept the first token given to you. Any after that may come from the
- * client.
+ *
+ * <p>The system guarantees that this method is called back between
+ * {@link InputMethodService#onCreate()} and {@link InputMethodService#onDestroy()}
+ * at most once.
*/
@MainThread
public void attachToken(IBinder token);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d3fd0cf..89d9ada 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -29,7 +30,6 @@
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -56,7 +56,8 @@
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
-import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputConnectionWrapper;
import com.android.internal.view.IInputContext;
@@ -407,6 +408,9 @@
final Pool<PendingEvent> mPendingEventPool = new SimplePool<>(20);
final SparseArray<PendingEvent> mPendingEvents = new SparseArray<>(20);
+ private final InputMethodPrivilegedOperationsRegistry mPrivOpsRegistry =
+ new InputMethodPrivilegedOperationsRegistry();
+
// -----------------------------------------------------------
static final int MSG_DUMP = 1;
@@ -773,19 +777,8 @@
* class are intended for app developers interacting with the IME.
*/
@Deprecated
- public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
- showStatusIconInternal(imeToken, packageName, iconId);
- }
-
- /**
- * @hide
- */
- public void showStatusIconInternal(IBinder imeToken, String packageName, int iconId) {
- try {
- mService.updateStatusIcon(imeToken, packageName, iconId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ public void showStatusIcon(IBinder imeToken, String packageName, @DrawableRes int iconId) {
+ mPrivOpsRegistry.get(imeToken).updateStatusIcon(packageName, iconId);
}
/**
@@ -795,36 +788,7 @@
*/
@Deprecated
public void hideStatusIcon(IBinder imeToken) {
- hideStatusIconInternal(imeToken);
- }
-
- /**
- * @hide
- */
- public void hideStatusIconInternal(IBinder imeToken) {
- try {
- mService.updateStatusIcon(imeToken, null, 0);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** @hide */
- public void setImeWindowStatus(IBinder imeToken, int vis, int backDisposition) {
- try {
- mService.setImeWindowStatus(imeToken, vis, backDisposition);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** @hide */
- public void reportStartInput(IBinder imeToken, IBinder startInputToken) {
- try {
- mService.reportStartInput(imeToken, startInputToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPrivOpsRegistry.get(imeToken).updateStatusIcon(null, 0);
}
/** @hide */
@@ -859,17 +823,6 @@
}
/**
- * @hide
- */
- public void reportFullscreenMode(IBinder token, boolean fullscreen) {
- try {
- mService.reportFullscreenMode(token, fullscreen);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Return true if the given view is the currently active view for the
* input method.
*/
@@ -1876,18 +1829,18 @@
*/
@Deprecated
public void setInputMethod(IBinder token, String id) {
- setInputMethodInternal(token, id);
- }
-
- /**
- * @hide
- */
- public void setInputMethodInternal(IBinder token, String id) {
- try {
- mService.setInputMethod(token, id);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (token == null) {
+ // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
+ // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // TODO(Bug 114488811): Consider deprecating null token rule.
+ try {
+ mService.setInputMethod(token, id);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return;
}
+ mPrivOpsRegistry.get(token).setInputMethod(id);
}
/**
@@ -1905,19 +1858,18 @@
*/
@Deprecated
public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
- setInputMethodAndSubtypeInternal(token, id, subtype);
- }
-
- /**
- * @hide
- */
- public void setInputMethodAndSubtypeInternal(
- IBinder token, String id, InputMethodSubtype subtype) {
- try {
- mService.setInputMethodAndSubtype(token, id, subtype);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ if (token == null) {
+ // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
+ // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // TODO(Bug 114488811): Consider deprecating null token rule.
+ try {
+ mService.setInputMethodAndSubtype(token, id, subtype);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return;
}
+ mPrivOpsRegistry.get(token).setInputMethodAndSubtype(id, subtype);
}
/**
@@ -1937,18 +1889,7 @@
*/
@Deprecated
public void hideSoftInputFromInputMethod(IBinder token, int flags) {
- hideSoftInputFromInputMethodInternal(token, flags);
- }
-
- /**
- * @hide
- */
- public void hideSoftInputFromInputMethodInternal(IBinder token, int flags) {
- try {
- mService.hideMySoftInput(token, flags);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPrivOpsRegistry.get(token).hideMySoftInput(flags);
}
/**
@@ -1969,18 +1910,7 @@
*/
@Deprecated
public void showSoftInputFromInputMethod(IBinder token, int flags) {
- showSoftInputFromInputMethodInternal(token, flags);
- }
-
- /**
- * @hide
- */
- public void showSoftInputFromInputMethodInternal(IBinder token, int flags) {
- try {
- mService.showMySoftInput(token, flags);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ mPrivOpsRegistry.get(token).showMySoftInput(flags);
}
/**
@@ -2178,15 +2108,13 @@
* @hide
*/
public void showInputMethodPicker(boolean showAuxiliarySubtypes) {
- synchronized (mH) {
- try {
- final int mode = showAuxiliarySubtypes ?
- SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES:
- SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
- mService.showInputMethodPickerFromClient(mClient, mode);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ final int mode = showAuxiliarySubtypes
+ ? SHOW_IM_PICKER_MODE_INCLUDE_AUXILIARY_SUBTYPES
+ : SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES;
+ try {
+ mService.showInputMethodPickerFromClient(mClient, mode);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2224,12 +2152,10 @@
* subtypes of all input methods will be shown.
*/
public void showInputMethodAndSubtypeEnabler(String imiId) {
- synchronized (mH) {
- try {
- mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ try {
+ mService.showInputMethodAndSubtypeEnablerFromClient(mClient, imiId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2254,12 +2180,10 @@
*/
@RequiresPermission(WRITE_SECURE_SETTINGS)
public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
- synchronized (mH) {
- try {
- return mService.setCurrentInputMethodSubtype(subtype);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ try {
+ return mService.setCurrentInputMethodSubtype(subtype);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2333,39 +2257,21 @@
}
/**
- * @return The current height of the input method window.
+ * This is kept due to {@link android.annotation.UnsupportedAppUsage}.
+ *
+ * <p>TODO(Bug 113914148): Check if we can remove this. We have accidentally exposed
+ * WindowManagerInternal#getInputMethodWindowVisibleHeight to app developers and some of them
+ * started relying on it.</p>
+ *
+ * @return Something that is not well-defined.
* @hide
*/
@UnsupportedAppUsage
public int getInputMethodWindowVisibleHeight() {
- synchronized (mH) {
- try {
- return mService.getInputMethodWindowVisibleHeight();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * Tells the system that the IME decided to not show a window and the system no longer needs to
- * use the previous IME's inset.
- *
- * <p>Caveat: {@link android.inputmethodservice.InputMethodService#clearInsetOfPreviousIme()}
- * is the only expected caller of this method. Do not depend on this anywhere else.</p>
- *
- * <p>TODO: We probably need to reconsider how IME should be handled.</p>
- * @hide
- * @param token Supplies the identifying token given to an input method when it was started,
- * which allows it to perform this operation on itself.
- */
- public void clearLastInputMethodWindowForTransition(final IBinder token) {
- synchronized (mH) {
- try {
- mService.clearLastInputMethodWindowForTransition(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ try {
+ return mService.getInputMethodWindowVisibleHeight();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -2383,20 +2289,17 @@
*/
@Deprecated
public boolean switchToLastInputMethod(IBinder imeToken) {
- return switchToPreviousInputMethodInternal(imeToken);
- }
-
- /**
- * @hide
- */
- public boolean switchToPreviousInputMethodInternal(IBinder imeToken) {
- synchronized (mH) {
+ if (imeToken == null) {
+ // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
+ // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // TODO(Bug 114488811): Consider deprecating null token rule.
try {
return mService.switchToPreviousInputMethod(imeToken);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ return mPrivOpsRegistry.get(imeToken).switchToPreviousInputMethod();
}
/**
@@ -2414,20 +2317,17 @@
*/
@Deprecated
public boolean switchToNextInputMethod(IBinder imeToken, boolean onlyCurrentIme) {
- return switchToNextInputMethodInternal(imeToken, onlyCurrentIme);
- }
-
- /**
- * @hide
- */
- public boolean switchToNextInputMethodInternal(IBinder imeToken, boolean onlyCurrentIme) {
- synchronized (mH) {
+ if (imeToken == null) {
+ // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission.
+ // Thus we cannot always rely on mPrivOpsRegistry unfortunately.
+ // TODO(Bug 114488811): Consider deprecating null token rule.
try {
return mService.switchToNextInputMethod(imeToken, onlyCurrentIme);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ return mPrivOpsRegistry.get(imeToken).switchToNextInputMethod(onlyCurrentIme);
}
/**
@@ -2446,20 +2346,7 @@
*/
@Deprecated
public boolean shouldOfferSwitchingToNextInputMethod(IBinder imeToken) {
- return shouldOfferSwitchingToNextInputMethodInternal(imeToken);
- }
-
- /**
- * @hide
- */
- public boolean shouldOfferSwitchingToNextInputMethodInternal(IBinder imeToken) {
- synchronized (mH) {
- try {
- return mService.shouldOfferSwitchingToNextInputMethod(imeToken);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
+ return mPrivOpsRegistry.get(imeToken).shouldOfferSwitchingToNextInputMethod();
}
/**
@@ -2488,57 +2375,19 @@
* @param subtypes subtypes will be added as additional subtypes of the current input method.
*/
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
- synchronized (mH) {
- try {
- mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ try {
+ mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
public InputMethodSubtype getLastInputMethodSubtype() {
- synchronized (mH) {
- try {
- return mService.getLastInputMethodSubtype();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
- }
-
- /**
- * Allow the receiver of {@link InputContentInfo} to obtain a temporary read-only access
- * permission to the content.
- *
- * <p>See {@link android.inputmethodservice.InputMethodService#exposeContent(InputContentInfo,
- * InputConnection)} for details.</p>
- *
- * @param token Supplies the identifying token given to an input method when it was started,
- * which allows it to perform this operation on itself.
- * @param inputContentInfo Content to be temporarily exposed from the input method to the
- * application.
- * This cannot be {@code null}.
- * @param editorInfo The editor that receives {@link InputContentInfo}.
- * @hide
- */
- public void exposeContent(@NonNull IBinder token, @NonNull InputContentInfo inputContentInfo,
- @NonNull EditorInfo editorInfo) {
- final IInputContentUriToken uriToken;
- final Uri contentUri = inputContentInfo.getContentUri();
try {
- uriToken = mService.createInputContentUriToken(token, contentUri,
- editorInfo.packageName);
- if (uriToken == null) {
- return;
- }
+ return mService.getLastInputMethodSubtype();
} catch (RemoteException e) {
- Log.e(TAG, "createInputContentAccessToken failed. contentUri=" + contentUri.toString()
- + " packageName=" + editorInfo.packageName, e);
- return;
+ throw e.rethrowFromSystemServer();
}
- inputContentInfo.setUriToken(uriToken);
- return;
}
void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -2637,4 +2486,34 @@
sb.append(",temporaryDetach=" + view.isTemporarilyDetached());
return sb.toString();
}
+
+ /**
+ * Called by {@link InputMethodService} so that API calls to deprecated ones defined in this
+ * class can be forwarded to {@link InputMethodPrivilegedOperations}.
+ *
+ * <p>Note: this method does not hold strong references to {@code token} and {@code ops}. The
+ * registry entry will be automatically cleared after {@code token} is garbage collected.</p>
+ *
+ * @param token IME token that is associated with {@code ops}
+ * @param ops {@link InputMethodPrivilegedOperations} that is associated with {@code token}
+ * @hide
+ */
+ public void registerInputMethodPrivOps(IBinder token, InputMethodPrivilegedOperations ops) {
+ mPrivOpsRegistry.put(token, ops);
+ }
+
+ /**
+ * Called from {@link InputMethodService#onDestroy()} to make sure that deprecated IME APIs
+ * defined in this class can no longer access to {@link InputMethodPrivilegedOperations}.
+ *
+ * <p>Note: Calling this method is optional, but at least gives more explict error message in
+ * logcat when IME developers are doing something unsupported (e.g. trying to call IME APIs
+ * after {@link InputMethodService#onDestroy()}).</p>
+ *
+ * @param token IME token to be removed.
+ * @hide
+ */
+ public void unregisterInputMethodPrivOps(IBinder token) {
+ mPrivOpsRegistry.remove(token);
+ }
}
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 3280d47..8e2786d 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -215,11 +215,11 @@
final Point windowCoords = getCurrentClampedWindowCoordinates();
final InternalPopupWindow currentWindowInstance = mWindow;
sPixelCopyHandlerThread.getThreadHandler().post(() -> {
- if (mWindow != currentWindowInstance) {
- // The magnifier was dismissed (and maybe shown again) in the meantime.
- return;
- }
synchronized (mLock) {
+ if (mWindow != currentWindowInstance) {
+ // The magnifier was dismissed (and maybe shown again) in the meantime.
+ return;
+ }
mWindow.setContentPositionForNextDraw(windowCoords.x, windowCoords.y);
}
});
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 0da47fd..1883809 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -545,7 +545,7 @@
StateListDrawable stateList = (StateListDrawable) mBackground;
// Find the above-anchor view - this one's easy, it should be labeled as such.
- int aboveAnchorStateIndex = stateList.getStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
+ int aboveAnchorStateIndex = stateList.findStateDrawableIndex(ABOVE_ANCHOR_STATE_SET);
// Now, for the below-anchor view, look for any other drawable specified in the
// StateListDrawable which is not for the above-anchor state and use that.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 31b1d99..0645a16 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -63,6 +63,7 @@
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
+import android.graphics.fonts.Font;
import android.graphics.fonts.FontVariationAxis;
import android.icu.text.DecimalFormatSymbols;
import android.os.AsyncTask;
@@ -2068,7 +2069,7 @@
*/
private void setTypefaceFromAttrs(@Nullable Typeface typeface, @Nullable String familyName,
@XMLTypefaceAttr int typefaceIndex, @Typeface.Style int style,
- @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+ @IntRange(from = -1, to = Font.FONT_WEIGHT_MAX) int weight) {
if (typeface == null && familyName != null) {
// Lookup normal Typeface from system font map.
final Typeface normalTypeface = Typeface.create(familyName, Typeface.NORMAL);
@@ -2095,9 +2096,9 @@
}
private void resolveStyleAndSetTypeface(@NonNull Typeface typeface, @Typeface.Style int style,
- @IntRange(from = -1, to = Typeface.MAX_WEIGHT) int weight) {
+ @IntRange(from = -1, to = Font.FONT_WEIGHT_MAX) int weight) {
if (weight >= 0) {
- weight = Math.min(Typeface.MAX_WEIGHT, weight);
+ weight = Math.min(Font.FONT_WEIGHT_MAX, weight);
final boolean italic = (style & Typeface.ITALIC) != 0;
setTypeface(Typeface.create(typeface, weight, italic));
} else {
@@ -3528,6 +3529,7 @@
boolean mHasLetterSpacing = false;
float mLetterSpacing = 0;
String mFontFeatureSettings = null;
+ String mFontVariationSettings = null;
@Override
public String toString() {
@@ -3555,6 +3557,7 @@
+ " mHasLetterSpacing:" + mHasLetterSpacing + "\n"
+ " mLetterSpacing:" + mLetterSpacing + "\n"
+ " mFontFeatureSettings:" + mFontFeatureSettings + "\n"
+ + " mFontVariationSettings:" + mFontVariationSettings + "\n"
+ "}";
}
}
@@ -3598,6 +3601,8 @@
com.android.internal.R.styleable.TextAppearance_letterSpacing);
sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFeatureSettings,
com.android.internal.R.styleable.TextAppearance_fontFeatureSettings);
+ sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontVariationSettings,
+ com.android.internal.R.styleable.TextAppearance_fontVariationSettings);
}
/**
@@ -3700,6 +3705,9 @@
case com.android.internal.R.styleable.TextAppearance_fontFeatureSettings:
attributes.mFontFeatureSettings = appearance.getString(attr);
break;
+ case com.android.internal.R.styleable.TextAppearance_fontVariationSettings:
+ attributes.mFontVariationSettings = appearance.getString(attr);
+ break;
default:
}
}
@@ -3756,6 +3764,10 @@
if (attributes.mFontFeatureSettings != null) {
setFontFeatureSettings(attributes.mFontFeatureSettings);
}
+
+ if (attributes.mFontVariationSettings != null) {
+ setFontVariationSettings(attributes.mFontVariationSettings);
+ }
}
/**
diff --git a/core/java/com/android/internal/app/ColorDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
index ba6cf26..7515180 100644
--- a/core/java/com/android/internal/app/ColorDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -112,8 +112,8 @@
private final Context mContext;
private final int mUserId;
- private final ContentObserver mContentObserver;
+ private ContentObserver mContentObserver;
private Callback mCallback;
private MetricsLogger mMetricsLogger;
@@ -124,18 +124,6 @@
public ColorDisplayController(@NonNull Context context, int userId) {
mContext = context.getApplicationContext();
mUserId = userId;
-
- mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- super.onChange(selfChange, uri);
-
- final String setting = uri == null ? null : uri.getLastPathSegment();
- if (setting != null) {
- onSettingChanged(setting);
- }
- }
- };
}
/**
@@ -522,6 +510,20 @@
if (oldCallback != callback) {
mCallback = callback;
+ if (mContentObserver == null) {
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ if (setting != null) {
+ onSettingChanged(setting);
+ }
+ }
+ };
+ }
+
if (callback == null) {
// Stop listening for changes now that there IS NOT a listener.
mContext.getContentResolver().unregisterContentObserver(mContentObserver);
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 115ba18..3842f66 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -20,8 +20,8 @@
import android.os.Parcel;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.service.procstats.ProcessStatsAssociationStateProto;
-import android.service.procstats.ProcessStatsStateProto;
+import android.service.procstats.PackageAssociationProcessStatsProto;
+import android.service.procstats.PackageAssociationSourceProcessStatsProto;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.TimeUtils;
@@ -662,22 +662,23 @@
public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
final long token = proto.start(fieldId);
- proto.write(ProcessStatsAssociationStateProto.COMPONENT_NAME, mName);
+ proto.write(PackageAssociationProcessStatsProto.COMPONENT_NAME, mName);
+
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
final SourceState src = mSources.valueAt(isrc);
- final long sourceToken = proto.start(ProcessStatsAssociationStateProto.SOURCES);
- proto.write(ProcessStatsAssociationStateProto.Source.PROCESS, key.mProcess);
- proto.write(ProcessStatsAssociationStateProto.Source.UID, key.mUid);
- proto.write(ProcessStatsAssociationStateProto.Source.TOTAL_COUNT, src.mCount);
+ final long sourceToken = proto.start(PackageAssociationProcessStatsProto.SOURCES);
+ proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_NAME, key.mProcess);
+ proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_UID, key.mUid);
+ proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_COUNT, src.mCount);
long duration = src.mDuration;
if (src.mNesting > 0) {
duration += now - src.mStartUptime;
}
- proto.write(ProcessStatsAssociationStateProto.Source.TOTAL_DURATION_MS, duration);
+ proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_DURATION_MS, duration);
if (src.mActiveCount != 0) {
- proto.write(ProcessStatsAssociationStateProto.Source.ACTIVE_COUNT,
+ proto.write(PackageAssociationSourceProcessStatsProto.ACTIVE_COUNT,
src.mActiveCount);
}
final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
@@ -690,16 +691,26 @@
duration += timeNow;
}
final int procState = SparseMappingTable.getIdFromKey(dkey);
- DumpUtils.printProcStateDurationProto(proto,
- ProcessStatsAssociationStateProto.Source.ACTIVE_STATES,
- procState, duration);
+ final long stateToken = proto.start(
+ PackageAssociationSourceProcessStatsProto.ACTIVE_STATE_STATS);
+ DumpUtils.printProto(proto,
+ PackageAssociationSourceProcessStatsProto.StateStats.PROCESS_STATE,
+ DumpUtils.STATE_PROTO_ENUMS, procState, 1);
+ proto.write(PackageAssociationSourceProcessStatsProto.StateStats.DURATION_MS,
+ duration);
+ proto.end(stateToken);
}
} else {
duration = src.mActiveDuration + timeNow;
if (duration != 0) {
- DumpUtils.printProcStateDurationProto(proto,
- ProcessStatsAssociationStateProto.Source.ACTIVE_STATES,
- src.mActiveProcState, duration);
+ final long stateToken = proto.start(
+ PackageAssociationSourceProcessStatsProto.ACTIVE_STATE_STATS);
+ DumpUtils.printProto(proto,
+ PackageAssociationSourceProcessStatsProto.StateStats.PROCESS_STATE,
+ DumpUtils.STATE_PROTO_ENUMS, src.mActiveProcState, 1);
+ proto.write(PackageAssociationSourceProcessStatsProto.StateStats.DURATION_MS,
+ duration);
+ proto.end(stateToken);
}
}
proto.end(sourceToken);
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index 701391d..eda04a6 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.app.procstats;
import android.os.UserHandle;
+import android.service.procstats.ProcessStatsEnums;
import android.service.procstats.ProcessStatsStateProto;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -107,20 +108,24 @@
STATE_TAGS[STATE_CACHED_EMPTY] = "e";
STATE_PROTO_ENUMS = new int[STATE_COUNT];
- STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsStateProto.PERSISTENT;
- STATE_PROTO_ENUMS[STATE_TOP] = ProcessStatsStateProto.TOP;
- STATE_PROTO_ENUMS[STATE_IMPORTANT_FOREGROUND] = ProcessStatsStateProto.IMPORTANT_FOREGROUND;
- STATE_PROTO_ENUMS[STATE_IMPORTANT_BACKGROUND] = ProcessStatsStateProto.IMPORTANT_BACKGROUND;
- STATE_PROTO_ENUMS[STATE_BACKUP] = ProcessStatsStateProto.BACKUP;
- STATE_PROTO_ENUMS[STATE_SERVICE] = ProcessStatsStateProto.SERVICE;
- STATE_PROTO_ENUMS[STATE_SERVICE_RESTARTING] = ProcessStatsStateProto.SERVICE_RESTARTING;
- STATE_PROTO_ENUMS[STATE_RECEIVER] = ProcessStatsStateProto.RECEIVER;
- STATE_PROTO_ENUMS[STATE_HEAVY_WEIGHT] = ProcessStatsStateProto.HEAVY_WEIGHT;
- STATE_PROTO_ENUMS[STATE_HOME] = ProcessStatsStateProto.HOME;
- STATE_PROTO_ENUMS[STATE_LAST_ACTIVITY] = ProcessStatsStateProto.LAST_ACTIVITY;
- STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY] = ProcessStatsStateProto.CACHED_ACTIVITY;
- STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] = ProcessStatsStateProto.CACHED_ACTIVITY_CLIENT;
- STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsStateProto.CACHED_EMPTY;
+ STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsEnums.PROCESS_STATE_PERSISTENT;
+ STATE_PROTO_ENUMS[STATE_TOP] = ProcessStatsEnums.PROCESS_STATE_TOP;
+ STATE_PROTO_ENUMS[STATE_IMPORTANT_FOREGROUND] =
+ ProcessStatsEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ STATE_PROTO_ENUMS[STATE_IMPORTANT_BACKGROUND] =
+ ProcessStatsEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
+ STATE_PROTO_ENUMS[STATE_BACKUP] = ProcessStatsEnums.PROCESS_STATE_BACKUP;
+ STATE_PROTO_ENUMS[STATE_SERVICE] = ProcessStatsEnums.PROCESS_STATE_SERVICE;
+ STATE_PROTO_ENUMS[STATE_SERVICE_RESTARTING] =
+ ProcessStatsEnums.PROCESS_STATE_SERVICE_RESTARTING;
+ STATE_PROTO_ENUMS[STATE_RECEIVER] = ProcessStatsEnums.PROCESS_STATE_RECEIVER;
+ STATE_PROTO_ENUMS[STATE_HEAVY_WEIGHT] = ProcessStatsEnums.PROCESS_STATE_HEAVY_WEIGHT;
+ STATE_PROTO_ENUMS[STATE_HOME] = ProcessStatsEnums.PROCESS_STATE_HOME;
+ STATE_PROTO_ENUMS[STATE_LAST_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_LAST_ACTIVITY;
+ STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY;
+ STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
+ ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+ STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
}
public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
@@ -138,8 +143,8 @@
};
static final int[] ADJ_SCREEN_PROTO_ENUMS = new int[] {
- ProcessStatsStateProto.OFF,
- ProcessStatsStateProto.ON
+ ProcessStatsEnums.SCREEN_STATE_OFF,
+ ProcessStatsEnums.SCREEN_STATE_ON
};
static final String[] ADJ_MEM_TAGS = new String[] {
@@ -147,10 +152,10 @@
};
static final int[] ADJ_MEM_PROTO_ENUMS = new int[] {
- ProcessStatsStateProto.NORMAL,
- ProcessStatsStateProto.MODERATE,
- ProcessStatsStateProto.LOW,
- ProcessStatsStateProto.CRITICAL
+ ProcessStatsEnums.MEMORY_STATE_NORMAL,
+ ProcessStatsEnums.MEMORY_STATE_MODERATE,
+ ProcessStatsEnums.MEMORY_STATE_LOW,
+ ProcessStatsEnums.MEMORY_STATE_CRITICAL
};
static final String CSV_SEP = "\t";
@@ -278,7 +283,6 @@
DumpUtils.STATE_PROTO_ENUMS, procState, 1);
proto.write(ProcessStatsStateProto.DURATION_MS, duration);
proto.end(stateToken);
-
}
public static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index d7f4736..4071c82 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -31,36 +31,38 @@
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
-import com.android.internal.app.procstats.ProcessStats.PackageState;
-import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder;
-import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection;
-import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
-import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
+
import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE;
+import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM;
import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_AVERAGE;
import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_MAXIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_RSS_MINIMUM;
+import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE;
import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM;
-import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT;
-import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
-import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
-import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
-import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
-import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
+import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM;
import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP;
-import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
-import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
-import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
-import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
-import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
-import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY;
import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT;
import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY;
import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND;
+import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY;
+import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT;
+import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE;
+import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING;
+import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
+
+import com.android.internal.app.procstats.ProcessStats.PackageState;
+import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder;
+import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection;
import java.io.PrintWriter;
import java.util.Comparator;
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index b6624a2..f6e99ba 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -2126,12 +2126,14 @@
for (int is = 0; is < mServices.size(); is++) {
final ServiceState serviceState = mServices.valueAt(is);
- serviceState.writeToProto(proto, ProcessStatsPackageProto.PROCESS_STATS, now);
+ serviceState.writeToProto(proto, ProcessStatsPackageProto.SERVICE_STATS,
+ now);
}
for (int ia=0; ia<mAssociations.size(); ia++) {
final AssociationState ascState = mAssociations.valueAt(ia);
- ascState.writeToProto(proto, ProcessStatsPackageProto.ASSOCIATION_STATS, now);
+ ascState.writeToProto(proto, ProcessStatsPackageProto.ASSOCIATION_STATS,
+ now);
}
proto.end(token);
diff --git a/core/java/com/android/internal/app/procstats/ServiceState.java b/core/java/com/android/internal/app/procstats/ServiceState.java
index 16975a4..72077c4 100644
--- a/core/java/com/android/internal/app/procstats/ServiceState.java
+++ b/core/java/com/android/internal/app/procstats/ServiceState.java
@@ -19,16 +19,13 @@
import android.os.Parcel;
import android.os.SystemClock;
-import android.service.procstats.ProcessStatsProto;
-import android.service.procstats.ProcessStatsServiceStateProto;
-import android.service.procstats.ProcessStatsStateProto;
+import android.service.procstats.PackageServiceOperationStatsProto;
+import android.service.procstats.PackageServiceStatsProto;
+import android.service.procstats.ProcessStatsEnums;
import android.util.Slog;
-import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import android.util.proto.ProtoUtils;
-import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT;
import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
import java.io.PrintWriter;
@@ -559,29 +556,45 @@
public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
final long token = proto.start(fieldId);
- proto.write(ProcessStatsServiceStateProto.SERVICE_NAME, mName);
- writeTypeToProto(proto, ProcessStatsServiceStateProto.RUNNING_OP,
+ proto.write(PackageServiceStatsProto.SERVICE_NAME, mName);
+
+ writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
+ ProcessStatsEnums.SERVICE_OPERATION_STATE_RUNNING,
ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
- writeTypeToProto(proto, ProcessStatsServiceStateProto.STARTED_OP,
+
+ writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
+ ProcessStatsEnums.SERVICE_OPERATION_STATE_STARTED,
ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now);
- writeTypeToProto(proto, ProcessStatsServiceStateProto.FOREGROUND_OP,
+
+ writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
+ ProcessStatsEnums.SERVICE_OPERATION_STATE_FOREGROUND,
ServiceState.SERVICE_FOREGROUND, mForegroundCount, mForegroundState,
mForegroundStartTime, now);
- writeTypeToProto(proto, ProcessStatsServiceStateProto.BOUND_OP,
+
+ writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
+ ProcessStatsEnums.SERVICE_OPERATION_STATE_BOUND,
ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now);
- writeTypeToProto(proto, ProcessStatsServiceStateProto.EXECUTING_OP,
+
+ writeTypeToProto(proto, PackageServiceStatsProto.OPERATION_STATS,
+ ProcessStatsEnums.SERVICE_OPERATION_STATE_EXECUTING,
ServiceState.SERVICE_EXEC, mExecCount, mExecState, mExecStartTime, now);
+
proto.end(token);
}
- public void writeTypeToProto(ProtoOutputStream proto, long fieldId, int serviceType,
+ /**
+ * write the metrics to proto for each operation type.
+ */
+ public void writeTypeToProto(ProtoOutputStream proto, long fieldId, int opType, int serviceType,
int opCount, int curState, long curStartTime, long now) {
if (opCount <= 0) {
return;
}
final long token = proto.start(fieldId);
- proto.write(ProcessStatsServiceStateProto.OperationInfo.COUNT, opCount);
+ proto.write(PackageServiceOperationStatsProto.OPERATION, opType);
+ proto.write(PackageServiceOperationStatsProto.COUNT, opCount);
+
boolean didCurState = false;
final int N = mDurations.getKeyCount();
for (int i=0; i<N; i++) {
@@ -597,21 +610,22 @@
didCurState = true;
time += now - curStartTime;
}
- final long stateToken = proto.start(ProcessStatsServiceStateProto.OperationInfo.STATES);
+ final long stateToken = proto.start(PackageServiceOperationStatsProto.STATE_STATS);
DumpUtils.printProcStateAdjTagProto(proto,
- ProcessStatsStateProto.SCREEN_STATE,
- ProcessStatsStateProto.MEMORY_STATE,
+ PackageServiceOperationStatsProto.StateStats.SCREEN_STATE,
+ PackageServiceOperationStatsProto.StateStats.MEMORY_STATE,
type);
- proto.write(ProcessStatsStateProto.DURATION_MS, time);
+ proto.write(PackageServiceOperationStatsProto.StateStats.DURATION_MS, time);
proto.end(stateToken);
}
if (!didCurState && curState != STATE_NOTHING) {
- final long stateToken = proto.start(ProcessStatsServiceStateProto.OperationInfo.STATES);
+ final long stateToken = proto.start(PackageServiceOperationStatsProto.STATE_STATS);
DumpUtils.printProcStateAdjTagProto(proto,
- ProcessStatsStateProto.SCREEN_STATE,
- ProcessStatsStateProto.MEMORY_STATE,
+ PackageServiceOperationStatsProto.StateStats.SCREEN_STATE,
+ PackageServiceOperationStatsProto.StateStats.MEMORY_STATE,
curState);
- proto.write(ProcessStatsStateProto.DURATION_MS, now - curStartTime);
+ proto.write(PackageServiceOperationStatsProto.StateStats.DURATION_MS,
+ now - curStartTime);
proto.end(stateToken);
}
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 8728367..cb282b6 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -75,6 +75,15 @@
return !TextUtils.isEmpty(reachSensorType());
}
+ public boolean wakeScreenGestureEnabled(int user) {
+ return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, user)
+ && wakeScreenGestureAvailable();
+ }
+
+ public boolean wakeScreenGestureAvailable() {
+ return !TextUtils.isEmpty(wakeScreenSensorType());
+ }
+
public String doubleTapSensorType() {
return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
}
@@ -87,6 +96,10 @@
return mContext.getResources().getString(R.string.config_dozeReachSensorType);
}
+ public String wakeScreenSensorType() {
+ return mContext.getResources().getString(R.string.config_dozeWakeScreenSensorType);
+ }
+
public boolean pulseOnLongPressEnabled(int user) {
return pulseOnLongPressAvailable() && boolSettingDefaultOff(
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
new file mode 100644
index 0000000..e998a52
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.net.Uri;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.inputmethod.IInputContentUriToken;
+
+/**
+ * Defines priviledged operations that only the current IME is allowed to call.
+ * Actual operations are implemented and handled by InputMethodManagerService.
+ */
+interface IInputMethodPrivilegedOperations {
+ void setImeWindowStatus(int vis, int backDisposition);
+ void reportStartInput(in IBinder startInputToken);
+ void clearLastInputMethodWindowForTransition();
+ IInputContentUriToken createInputContentUriToken(in Uri contentUri, in String packageName);
+ void reportFullscreenMode(boolean fullscreen);
+ void setInputMethod(String id);
+ void setInputMethodAndSubtype(String id, in InputMethodSubtype subtype);
+ void hideMySoftInput(int flags);
+ void showMySoftInput(int flags);
+ void updateStatusIcon(String packageName, int iconId);
+ boolean switchToPreviousInputMethod();
+ boolean switchToNextInputMethod(boolean onlyCurrentIme);
+ boolean shouldOfferSwitchingToNextInputMethod();
+}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
new file mode 100644
index 0000000..7891540
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -0,0 +1,349 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A utility class to take care of boilerplate code around IPCs.
+ */
+public final class InputMethodPrivilegedOperations {
+ private static final String TAG = "InputMethodPrivilegedOperations";
+
+ private static final class OpsHolder {
+ @Nullable
+ @GuardedBy("this")
+ private IInputMethodPrivilegedOperations mPrivOps;
+
+ /**
+ * Sets {@link IInputMethodPrivilegedOperations}.
+ *
+ * <p>This method can be called only once.</p>
+ *
+ * @param privOps Binder interface to be set
+ */
+ @AnyThread
+ public synchronized void set(IInputMethodPrivilegedOperations privOps) {
+ if (mPrivOps != null) {
+ throw new IllegalStateException(
+ "IInputMethodPrivilegedOperations must be set at most once."
+ + " privOps=" + privOps);
+ }
+ mPrivOps = privOps;
+ }
+
+ /**
+ * A simplified version of {@link android.os.Debug#getCaller()}.
+ *
+ * @return method name of the caller.
+ */
+ @AnyThread
+ private static String getCallerMethodName() {
+ final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+ if (callStack.length <= 4) {
+ return "<bottom of call stack>";
+ }
+ return callStack[4].getMethodName();
+ }
+
+ @AnyThread
+ @Nullable
+ public synchronized IInputMethodPrivilegedOperations getAndWarnIfNull() {
+ if (mPrivOps == null) {
+ Log.e(TAG, getCallerMethodName() + " is ignored."
+ + " Call it within attachToken() and InputMethodService.onDestroy()");
+ }
+ return mPrivOps;
+ }
+ }
+ private final OpsHolder mOps = new OpsHolder();
+
+ /**
+ * Sets {@link IInputMethodPrivilegedOperations}.
+ *
+ * <p>This method can be called only once.</p>
+ *
+ * @param privOps Binder interface to be set
+ */
+ @AnyThread
+ public void set(IInputMethodPrivilegedOperations privOps) {
+ mOps.set(privOps);
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#setImeWindowStatus(int, int)}.
+ *
+ * @param vis visibility flags
+ * @param backDisposition disposition flags
+ * @see android.inputmethodservice.InputMethodService#IME_ACTIVE
+ * @see android.inputmethodservice.InputMethodService#IME_VISIBLE
+ * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT
+ * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING
+ */
+ @AnyThread
+ public void setImeWindowStatus(int vis, int backDisposition) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.setImeWindowStatus(vis, backDisposition);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#reportStartInput(IBinder)}.
+ *
+ * @param startInputToken {@link IBinder} token to distinguish startInput session
+ */
+ @AnyThread
+ public void reportStartInput(IBinder startInputToken) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.reportStartInput(startInputToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#clearLastInputMethodWindowForTransition()}.
+ */
+ @AnyThread
+ public void clearLastInputMethodWindowForTransition() {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.clearLastInputMethodWindowForTransition();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#createInputContentUriToken(Uri, String)}.
+ *
+ * @param contentUri Content URI to which a temporary read permission should be granted
+ * @param packageName Indicates what package needs to have a temporary read permission
+ * @return special Binder token that should be set to
+ * {@link android.view.inputmethod.InputContentInfo#setUriToken(IInputContentUriToken)}
+ */
+ @AnyThread
+ public IInputContentUriToken createInputContentUriToken(Uri contentUri, String packageName) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return null;
+ }
+ try {
+ return ops.createInputContentUriToken(contentUri, packageName);
+ } catch (RemoteException e) {
+ // For historical reasons, this error was silently ignored.
+ // Note that the caller already logs error so we do not need additional Log.e() here.
+ // TODO(team): Check if it is safe to rethrow error here.
+ return null;
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#reportFullscreenMode(boolean)}.
+ *
+ * @param fullscreen {@code true} if the IME enters full screen mode
+ */
+ @AnyThread
+ public void reportFullscreenMode(boolean fullscreen) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.reportFullscreenMode(fullscreen);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#updateStatusIcon(String, int)}.
+ *
+ * @param packageName package name from which the status icon should be loaded
+ * @param iconResId resource ID of the icon to be loaded
+ */
+ @AnyThread
+ public void updateStatusIcon(String packageName, @DrawableRes int iconResId) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.updateStatusIcon(packageName, iconResId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#setInputMethod(String)}.
+ *
+ * @param id IME ID of the IME to switch to
+ * @see android.view.inputmethod.InputMethodInfo#getId()
+ */
+ @AnyThread
+ public void setInputMethod(String id) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.setInputMethod(id);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#setInputMethodAndSubtype(String,
+ * InputMethodSubtype)}
+ *
+ * @param id IME ID of the IME to switch to
+ * @param subtype {@link InputMethodSubtype} to switch to
+ * @see android.view.inputmethod.InputMethodInfo#getId()
+ */
+ @AnyThread
+ public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.setInputMethodAndSubtype(id, subtype);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int)}
+ *
+ * @param flags additional operating flags
+ * @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY
+ * @see android.view.inputmethod.InputMethodManager#HIDE_NOT_ALWAYS
+ */
+ @AnyThread
+ public void hideMySoftInput(int flags) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.hideMySoftInput(flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int)}
+ *
+ * @param flags additional operating flags
+ * @see android.view.inputmethod.InputMethodManager#SHOW_IMPLICIT
+ * @see android.view.inputmethod.InputMethodManager#SHOW_FORCED
+ */
+ @AnyThread
+ public void showMySoftInput(int flags) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.showMySoftInput(flags);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#switchToPreviousInputMethod()}
+ *
+ * @return {@code true} if handled
+ */
+ @AnyThread
+ public boolean switchToPreviousInputMethod() {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return false;
+ }
+ try {
+ return ops.switchToPreviousInputMethod();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#switchToNextInputMethod(boolean)}
+ *
+ * @param onlyCurrentIme {@code true} to switch to a {@link InputMethodSubtype} within the same
+ * IME
+ * @return {@code true} if handled
+ */
+ @AnyThread
+ public boolean switchToNextInputMethod(boolean onlyCurrentIme) {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return false;
+ }
+ try {
+ return ops.switchToNextInputMethod(onlyCurrentIme);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IInputMethodPrivilegedOperations#shouldOfferSwitchingToNextInputMethod()}
+ *
+ * @return {@code true} if the IEM should offer a way to globally switch IME
+ */
+ @AnyThread
+ public boolean shouldOfferSwitchingToNextInputMethod() {
+ final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return false;
+ }
+ try {
+ return ops.shouldOfferSwitchingToNextInputMethod();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
new file mode 100644
index 0000000..3255153
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperationsRegistry.java
@@ -0,0 +1,116 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+import java.util.WeakHashMap;
+
+/**
+ * A weak-reference-based mapper from IME token to {@link InputMethodPrivilegedOperations} that is
+ * used only to support deprecated IME APIs in {@link android.view.inputmethod.InputMethodManager}.
+ */
+public final class InputMethodPrivilegedOperationsRegistry {
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final WeakHashMap<IBinder, WeakReference<InputMethodPrivilegedOperations>>
+ mRegistry = new WeakHashMap<>();
+
+ @Nullable
+ private static InputMethodPrivilegedOperations sNop;
+
+ @NonNull
+ @AnyThread
+ private static InputMethodPrivilegedOperations getNopOps() {
+ // Strict thread-safety is not necessary because temporarily creating multiple nop instance
+ // is basically harmless
+ if (sNop == null) {
+ sNop = new InputMethodPrivilegedOperations();
+ }
+ return sNop;
+ }
+
+ /**
+ * Put a new entry to the registry.
+ *
+ * <p>Note: {@link InputMethodPrivilegedOperationsRegistry} does not hold strong reference to
+ * {@code token} and {@code ops}. The caller must be responsible for holding strong references
+ * to those objects, that is until {@link android.inputmethodservice.InputMethodService} is
+ * destroyed.</p>
+ *
+ * @param token IME token
+ * @param ops {@link InputMethodPrivilegedOperations} to be associated with the given IME token
+ */
+ @AnyThread
+ public void put(IBinder token, InputMethodPrivilegedOperations ops) {
+ synchronized (mLock) {
+ final WeakReference<InputMethodPrivilegedOperations> previousOps =
+ mRegistry.put(token, new WeakReference<>(ops));
+ if (previousOps != null) {
+ throw new IllegalStateException(previousOps.get() + " is already registered for "
+ + " this token=" + token + " newOps=" + ops);
+ }
+ }
+ }
+
+ /**
+ * Get a {@link InputMethodPrivilegedOperations} from the given IME token. If it is not
+ * available, return a fake instance that does nothing except for showing warning messages.
+ *
+ * @param token IME token
+ * @return real {@link InputMethodPrivilegedOperations} object if {@code token} is still valid.
+ * Otherwise a fake instance of {@link InputMethodPrivilegedOperations} hat does nothing
+ * except for showing warning messages
+ */
+ @NonNull
+ @AnyThread
+ public InputMethodPrivilegedOperations get(IBinder token) {
+ synchronized (mLock) {
+ final WeakReference<InputMethodPrivilegedOperations> wrapperRef = mRegistry.get(token);
+ if (wrapperRef == null) {
+ return getNopOps();
+ }
+ final InputMethodPrivilegedOperations wrapper = wrapperRef.get();
+ if (wrapper == null) {
+ return getNopOps();
+ }
+ return wrapper;
+ }
+ }
+
+ /**
+ * Explicitly removes the specified entry.
+ *
+ * <p>Note: Calling this method is optional. In general, {@link WeakHashMap} and
+ * {@link WeakReference} guarantee that the entry will be removed after specified binder proxies
+ * are garbage collected.</p>
+ *
+ * @param token IME token to be removed.
+ */
+ @AnyThread
+ public void remove(IBinder token) {
+ synchronized (mLock) {
+ mRegistry.remove(token);
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 4aa30f6..c0c358d 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -16,16 +16,9 @@
package com.android.internal.os;
+import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.BatteryManager;
-import android.os.BatteryManagerInternal;
import android.os.Binder;
-import android.os.OsProtoEnums;
-import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateFormat;
@@ -37,7 +30,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BinderInternal.CallSession;
-import com.android.server.LocalServices;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
@@ -63,7 +55,6 @@
private static final String TAG = "BinderCallsStats";
private static final int CALL_SESSIONS_POOL_SIZE = 100;
- private static final int PERIODIC_SAMPLING_INTERVAL = 10;
private static final int MAX_EXCEPTION_COUNT_SIZE = 50;
private static final String EXCEPTION_COUNT_OVERFLOW_NAME = "overflow";
@@ -81,25 +72,7 @@
private final Random mRandom;
private long mStartTime = System.currentTimeMillis();
- // State updated by the broadcast receiver below.
- private boolean mScreenInteractive;
- private boolean mCharging;
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- switch (intent.getAction()) {
- case Intent.ACTION_BATTERY_CHANGED:
- mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
- break;
- case Intent.ACTION_SCREEN_ON:
- mScreenInteractive = true;
- break;
- case Intent.ACTION_SCREEN_OFF:
- mScreenInteractive = false;
- break;
- }
- }
- };
+ private CachedDeviceState.Readonly mDeviceState;
/** Injector for {@link BinderCallsStats}. */
public static class Injector {
@@ -112,65 +85,14 @@
this.mRandom = injector.getRandomGenerator();
}
- public void systemReady(Context context) {
- registerBroadcastReceiver(context);
- setInitialState(queryScreenInteractive(context), queryIsCharging());
- }
-
- /**
- * Listens for screen/battery state changes.
- */
- @VisibleForTesting
- public void registerBroadcastReceiver(Context context) {
- final IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- context.registerReceiver(mBroadcastReceiver, filter);
- }
-
- /**
- * Sets the battery/screen initial state.
- *
- * This has to be updated *after* the broadcast receiver is installed.
- */
- @VisibleForTesting
- public void setInitialState(boolean isScreenInteractive, boolean isCharging) {
- this.mScreenInteractive = isScreenInteractive;
- this.mCharging = isCharging;
- // Data collected previously was not accurate since the battery/screen state was not set.
- reset();
- }
-
- private boolean queryIsCharging() {
- final BatteryManagerInternal batteryManager =
- LocalServices.getService(BatteryManagerInternal.class);
- if (batteryManager == null) {
- Slog.wtf(TAG, "BatteryManager null while starting BinderCallsStatsService");
- // Default to true to not collect any data.
- return true;
- } else {
- return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
- }
- }
-
- private boolean queryScreenInteractive(Context context) {
- final PowerManager powerManager = context.getSystemService(PowerManager.class);
- final boolean screenInteractive;
- if (powerManager == null) {
- Slog.wtf(TAG, "PowerManager null while starting BinderCallsStatsService",
- new Throwable());
- return true;
- } else {
- return powerManager.isInteractive();
- }
+ public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ mDeviceState = deviceState;
}
@Override
@Nullable
public CallSession callStarted(Binder binder, int code) {
- if (mCharging) {
+ if (mDeviceState == null || mDeviceState.isCharging()) {
return null;
}
@@ -221,7 +143,7 @@
synchronized (mLock) {
// This was already checked in #callStart but check again while synchronized.
- if (mCharging) {
+ if (mDeviceState == null || mDeviceState.isCharging()) {
return;
}
@@ -233,7 +155,7 @@
uidEntry.recordedCallCount++;
final CallStat callStat = uidEntry.getOrCreate(
- s.binderClass, s.transactionCode, mScreenInteractive);
+ s.binderClass, s.transactionCode, mDeviceState.isScreenInteractive());
callStat.callCount++;
callStat.recordedCallCount++;
callStat.cpuTimeMicros += duration;
@@ -252,7 +174,7 @@
// Only record the total call count if we already track data for this key.
// It helps to keep the memory usage down when sampling is enabled.
final CallStat callStat = uidEntry.get(
- s.binderClass, s.transactionCode, mScreenInteractive);
+ s.binderClass, s.transactionCode, mDeviceState.isScreenInteractive());
if (callStat != null) {
callStat.callCount++;
}
@@ -319,13 +241,13 @@
public ArrayList<ExportedCallStat> getExportedCallStats() {
// We do not collect all the data if detailed tracking is off.
if (!mDetailedTracking) {
- return new ArrayList<ExportedCallStat>();
+ return new ArrayList<>();
}
ArrayList<ExportedCallStat> resultCallStats = new ArrayList<>();
synchronized (mLock) {
final int uidEntriesSize = mUidEntries.size();
- for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++){
+ for (int entryIdx = 0; entryIdx < uidEntriesSize; entryIdx++) {
final UidEntry entry = mUidEntries.valueAt(entryIdx);
for (CallStat stat : entry.getCallStatsList()) {
ExportedCallStat exported = new ExportedCallStat();
@@ -387,13 +309,15 @@
}
}
- public void dump(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
+ /** Writes the collected statistics to the supplied {@link PrintWriter}.*/
+ public void dump(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap, boolean verbose) {
synchronized (mLock) {
dumpLocked(pw, appIdToPkgNameMap, verbose);
}
}
- private void dumpLocked(PrintWriter pw, Map<Integer,String> appIdToPkgNameMap, boolean verbose) {
+ private void dumpLocked(PrintWriter pw, Map<Integer, String> appIdToPkgNameMap,
+ boolean verbose) {
long totalCallsCount = 0;
long totalRecordedCallsCount = 0;
long totalCpuTime = 0;
@@ -450,13 +374,13 @@
for (UidEntry entry : summaryEntries) {
String uidStr = uidToString(entry.uid, appIdToPkgNameMap);
pw.println(String.format(" %10d %3.0f%% %8d %8d %s",
- entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
- entry.recordedCallCount, entry.callCount, uidStr));
+ entry.cpuTimeMicros, 100d * entry.cpuTimeMicros / totalCpuTime,
+ entry.recordedCallCount, entry.callCount, uidStr));
}
pw.println();
pw.println(String.format(" Summary: total_cpu_time=%d, "
- + "calls_count=%d, avg_call_cpu_time=%.0f",
- totalCpuTime, totalCallsCount, (double)totalCpuTime / totalRecordedCallsCount));
+ + "calls_count=%d, avg_call_cpu_time=%.0f",
+ totalCpuTime, totalCallsCount, (double) totalCpuTime / totalRecordedCallsCount));
pw.println();
pw.println("Exceptions thrown (exception_count, class_name):");
@@ -723,11 +647,6 @@
return result;
}
- @VisibleForTesting
- public BroadcastReceiver getBroadcastReceiver() {
- return mBroadcastReceiver;
- }
-
private static int compareByCpuDesc(
ExportedCallStat a, ExportedCallStat b) {
return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros);
diff --git a/core/java/com/android/internal/os/CachedDeviceState.java b/core/java/com/android/internal/os/CachedDeviceState.java
new file mode 100644
index 0000000..8c90682
--- /dev/null
+++ b/core/java/com/android/internal/os/CachedDeviceState.java
@@ -0,0 +1,66 @@
+/*
+ * 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.internal.os;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Stores the device state (e.g. charging/on battery, screen on/off) to be shared with
+ * the System Server telemetry services.
+ *
+ * @hide
+ */
+public class CachedDeviceState {
+ private volatile boolean mScreenInteractive;
+ private volatile boolean mCharging;
+
+ public CachedDeviceState() {
+ mCharging = true;
+ mScreenInteractive = false;
+ }
+
+ @VisibleForTesting
+ public CachedDeviceState(boolean isCharging, boolean isScreenInteractive) {
+ mCharging = isCharging;
+ mScreenInteractive = isScreenInteractive;
+ }
+
+ public void setScreenInteractive(boolean screenInteractive) {
+ mScreenInteractive = screenInteractive;
+ }
+
+ public void setCharging(boolean charging) {
+ mCharging = charging;
+ }
+
+ public Readonly getReadonlyClient() {
+ return new CachedDeviceState.Readonly();
+ }
+
+ /**
+ * Allows for only a readonly access to the device state.
+ */
+ public class Readonly {
+ public boolean isCharging() {
+ return mCharging;
+ }
+
+ public boolean isScreenInteractive() {
+ return mScreenInteractive;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/LooperStats.java b/core/java/com/android/internal/os/LooperStats.java
new file mode 100644
index 0000000..02a8b22
--- /dev/null
+++ b/core/java/com/android/internal/os/LooperStats.java
@@ -0,0 +1,292 @@
+/*
+ * 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.internal.os;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ThreadLocalRandom;
+
+/**
+ * Collects aggregated telemetry data about Looper message dispatching.
+ *
+ * @hide Only for use within the system server.
+ */
+public class LooperStats implements Looper.Observer {
+ private static final int TOKEN_POOL_SIZE = 50;
+
+ @GuardedBy("mLock")
+ private final SparseArray<Entry> mEntries = new SparseArray<>(512);
+ private final Object mLock = new Object();
+ private final Entry mOverflowEntry = new Entry("OVERFLOW");
+ private final Entry mHashCollisionEntry = new Entry("HASH_COLLISION");
+ private final ConcurrentLinkedQueue<DispatchSession> mSessionPool =
+ new ConcurrentLinkedQueue<>();
+ private final int mEntriesSizeCap;
+ private int mSamplingInterval;
+ private CachedDeviceState.Readonly mDeviceState;
+
+ public LooperStats(int samplingInterval, int entriesSizeCap) {
+ this.mSamplingInterval = samplingInterval;
+ this.mEntriesSizeCap = entriesSizeCap;
+ }
+
+ public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) {
+ mDeviceState = deviceState;
+ }
+
+ @Override
+ public Object messageDispatchStarting() {
+ if (deviceStateAllowsCollection() && shouldCollectDetailedData()) {
+ DispatchSession session = mSessionPool.poll();
+ session = session == null ? new DispatchSession() : session;
+ session.startTimeMicro = getElapsedRealtimeMicro();
+ session.cpuStartMicro = getThreadTimeMicro();
+ return session;
+ }
+
+ return DispatchSession.NOT_SAMPLED;
+ }
+
+ @Override
+ public void messageDispatched(Object token, Message msg) {
+ if (!deviceStateAllowsCollection()) {
+ return;
+ }
+
+ DispatchSession session = (DispatchSession) token;
+ Entry entry = getOrCreateEntry(msg);
+ synchronized (entry) {
+ entry.messageCount++;
+ if (session != DispatchSession.NOT_SAMPLED) {
+ entry.recordedMessageCount++;
+ long latency = getElapsedRealtimeMicro() - session.startTimeMicro;
+ long cpuUsage = getThreadTimeMicro() - session.cpuStartMicro;
+ entry.totalLatencyMicro += latency;
+ entry.maxLatencyMicro = Math.max(entry.maxLatencyMicro, latency);
+ entry.cpuUsageMicro += cpuUsage;
+ entry.maxCpuUsageMicro = Math.max(entry.maxCpuUsageMicro, cpuUsage);
+ }
+ }
+
+ recycleSession(session);
+ }
+
+ @Override
+ public void dispatchingThrewException(Object token, Message msg, Exception exception) {
+ if (!deviceStateAllowsCollection()) {
+ return;
+ }
+
+ DispatchSession session = (DispatchSession) token;
+ Entry entry = getOrCreateEntry(msg);
+ synchronized (entry) {
+ entry.exceptionCount++;
+ }
+ recycleSession(session);
+ }
+
+ private boolean deviceStateAllowsCollection() {
+ // Do not collect data if on charger or the state is not set.
+ return mDeviceState != null && !mDeviceState.isCharging();
+ }
+
+ /** Returns an array of {@link ExportedEntry entries} with the aggregated statistics. */
+ public List<ExportedEntry> getEntries() {
+ final ArrayList<ExportedEntry> entries;
+ synchronized (mLock) {
+ final int size = mEntries.size();
+ entries = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Entry entry = mEntries.valueAt(i);
+ synchronized (entry) {
+ entries.add(new ExportedEntry(entry));
+ }
+ }
+ }
+ // Add the overflow and collision entries only if they have any data.
+ if (mOverflowEntry.messageCount > 0 || mOverflowEntry.exceptionCount > 0) {
+ synchronized (mOverflowEntry) {
+ entries.add(new ExportedEntry(mOverflowEntry));
+ }
+ }
+ if (mHashCollisionEntry.messageCount > 0 || mHashCollisionEntry.exceptionCount > 0) {
+ synchronized (mHashCollisionEntry) {
+ entries.add(new ExportedEntry(mHashCollisionEntry));
+ }
+ }
+ return entries;
+ }
+
+ /** Removes all collected data. */
+ public void reset() {
+ synchronized (mLock) {
+ mEntries.clear();
+ }
+ synchronized (mHashCollisionEntry) {
+ mHashCollisionEntry.reset();
+ }
+ synchronized (mOverflowEntry) {
+ mOverflowEntry.reset();
+ }
+ }
+
+ public void setSamplingInterval(int samplingInterval) {
+ mSamplingInterval = samplingInterval;
+ }
+
+ @NonNull
+ private Entry getOrCreateEntry(Message msg) {
+ final boolean isInteractive = mDeviceState.isScreenInteractive();
+ final int id = Entry.idFor(msg, isInteractive);
+ Entry entry;
+ synchronized (mLock) {
+ entry = mEntries.get(id);
+ if (entry == null) {
+ if (mEntries.size() >= mEntriesSizeCap) {
+ // If over the size cap, track totals under a single entry.
+ return mOverflowEntry;
+ }
+ entry = new Entry(msg, isInteractive);
+ mEntries.put(id, entry);
+ }
+ }
+
+ if (entry.handler.getClass() != msg.getTarget().getClass()
+ || entry.handler.getLooper().getThread() != msg.getTarget().getLooper().getThread()
+ || entry.isInteractive != isInteractive) {
+ // If a hash collision happened, track totals under a single entry.
+ return mHashCollisionEntry;
+ }
+ return entry;
+ }
+
+ private void recycleSession(DispatchSession session) {
+ if (session != DispatchSession.NOT_SAMPLED && mSessionPool.size() < TOKEN_POOL_SIZE) {
+ mSessionPool.add(session);
+ }
+ }
+
+ protected long getThreadTimeMicro() {
+ return SystemClock.currentThreadTimeMicro();
+ }
+
+ protected long getElapsedRealtimeMicro() {
+ return SystemClock.elapsedRealtimeNanos() / 1000;
+ }
+
+ protected boolean shouldCollectDetailedData() {
+ return ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+ }
+
+ private static class DispatchSession {
+ static final DispatchSession NOT_SAMPLED = new DispatchSession();
+ public long startTimeMicro;
+ public long cpuStartMicro;
+ }
+
+ private static class Entry {
+ public final Handler handler;
+ public final String messageName;
+ public final boolean isInteractive;
+ public long messageCount;
+ public long recordedMessageCount;
+ public long exceptionCount;
+ public long totalLatencyMicro;
+ public long maxLatencyMicro;
+ public long cpuUsageMicro;
+ public long maxCpuUsageMicro;
+
+ Entry(Message msg, boolean isInteractive) {
+ this.handler = msg.getTarget();
+ this.messageName = handler.getMessageName(msg);
+ this.isInteractive = isInteractive;
+ }
+
+ Entry(String specialEntryName) {
+ this.messageName = specialEntryName;
+ this.handler = null;
+ this.isInteractive = false;
+ }
+
+ void reset() {
+ messageCount = 0;
+ recordedMessageCount = 0;
+ exceptionCount = 0;
+ totalLatencyMicro = 0;
+ maxLatencyMicro = 0;
+ cpuUsageMicro = 0;
+ maxCpuUsageMicro = 0;
+ }
+
+ static int idFor(Message msg, boolean isInteractive) {
+ int result = 7;
+ result = 31 * result + msg.getTarget().getLooper().getThread().hashCode();
+ result = 31 * result + msg.getTarget().getClass().hashCode();
+ result = 31 * result + (isInteractive ? 1231 : 1237);
+ if (msg.getCallback() != null) {
+ return 31 * result + msg.getCallback().getClass().hashCode();
+ } else {
+ return 31 * result + msg.what;
+ }
+ }
+ }
+
+ /** Aggregated data of Looper message dispatching in the in the current process. */
+ public static class ExportedEntry {
+ public final String handlerClassName;
+ public final String threadName;
+ public final String messageName;
+ public final boolean isInteractive;
+ public final long messageCount;
+ public final long recordedMessageCount;
+ public final long exceptionCount;
+ public final long totalLatencyMicros;
+ public final long maxLatencyMicros;
+ public final long cpuUsageMicros;
+ public final long maxCpuUsageMicros;
+
+ ExportedEntry(Entry entry) {
+ if (entry.handler != null) {
+ this.handlerClassName = entry.handler.getClass().getName();
+ this.threadName = entry.handler.getLooper().getThread().getName();
+ } else {
+ // Overflow/collision entries do not have a handler set.
+ this.handlerClassName = "";
+ this.threadName = "";
+ }
+ this.isInteractive = entry.isInteractive;
+ this.messageName = entry.messageName;
+ this.messageCount = entry.messageCount;
+ this.recordedMessageCount = entry.recordedMessageCount;
+ this.exceptionCount = entry.exceptionCount;
+ this.totalLatencyMicros = entry.totalLatencyMicro;
+ this.maxLatencyMicros = entry.maxLatencyMicro;
+ this.cpuUsageMicros = entry.cpuUsageMicro;
+ this.maxCpuUsageMicros = entry.maxCpuUsageMicro;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 189c8d5..d4c4ab3 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -16,9 +16,6 @@
package com.android.internal.view;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.SomeArgs;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
@@ -37,6 +34,9 @@
import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
import android.view.inputmethod.InputContentInfo;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.SomeArgs;
+
public abstract class IInputConnectionWrapper extends IInputContext.Stub {
private static final String TAG = "IInputConnectionWrapper";
private static final boolean DEBUG = false;
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 5b29f4c..b6a654a 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -22,6 +22,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodSession;
import com.android.internal.view.IInputSessionCallback;
@@ -32,7 +33,7 @@
* {@hide}
*/
oneway interface IInputMethod {
- void attachToken(IBinder token);
+ void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps);
void bindInput(in InputBinding binding);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 4b004e2..5afc53f 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -16,13 +16,12 @@
package com.android.internal.view;
-import android.net.Uri;
import android.os.ResultReceiver;
import android.text.style.SuggestionSpan;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
-import com.android.internal.inputmethod.IInputContentUriToken;
+
import com.android.internal.view.InputBindResult;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
@@ -64,28 +63,22 @@
int auxiliarySubtypeMode);
void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId);
boolean isInputMethodPickerShownForTest();
+ // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
void setInputMethod(in IBinder token, String id);
+ // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
void setInputMethodAndSubtype(in IBinder token, String id, in InputMethodSubtype subtype);
- void hideMySoftInput(in IBinder token, int flags);
- void showMySoftInput(in IBinder token, int flags);
- void updateStatusIcon(in IBinder token, String packageName, int iconId);
- void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
- void reportStartInput(in IBinder token, in IBinder startInputToken);
void registerSuggestionSpansForNotification(in SuggestionSpan[] spans);
boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index);
InputMethodSubtype getCurrentInputMethodSubtype();
boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype);
+ // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
boolean switchToPreviousInputMethod(in IBinder token);
+ // TODO(Bug 114488811): this can be removed once we deprecate special null token rule.
boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
- boolean shouldOfferSwitchingToNextInputMethod(in IBinder token);
void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
+ // This is kept due to @UnsupportedAppUsage.
+ // TODO(Bug 113914148): Consider removing this.
int getInputMethodWindowVisibleHeight();
- void clearLastInputMethodWindowForTransition(in IBinder token);
-
- IInputContentUriToken createInputContentUriToken(in IBinder token, in Uri contentUri,
- in String packageName);
-
- void reportFullscreenMode(in IBinder token, boolean fullscreen);
oneway void notifyUserAction(int sequenceNumber);
}
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 4ee951a..0b37d57 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -19,8 +19,8 @@
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.graphics.RectF;
import android.graphics.Paint.FontMetricsInt;
+import android.graphics.RectF;
import android.hardware.input.InputManager;
import android.hardware.input.InputManager.InputDeviceListener;
import android.os.SystemProperties;
@@ -29,12 +29,12 @@
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.MotionEvent.PointerCoords;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import android.view.MotionEvent.PointerCoords;
import java.util.ArrayList;
@@ -105,10 +105,6 @@
}
}
- private final int ESTIMATE_PAST_POINTS = 4;
- private final int ESTIMATE_FUTURE_POINTS = 2;
- private final float ESTIMATE_INTERVAL = 0.02f;
-
private final InputManager mIm;
private final ViewConfiguration mVC;
@@ -336,37 +332,14 @@
}
if (drawn) {
- // Draw movement estimate curve.
- mPaint.setARGB(128, 128, 0, 128);
- float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
- float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
- for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
- float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL);
- float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL);
- canvas.drawLine(lx, ly, x, y, mPaint);
- lx = x;
- ly = y;
- }
-
// Draw velocity vector.
mPaint.setARGB(255, 255, 64, 128);
float xVel = ps.mXVelocity * (1000 / 60);
float yVel = ps.mYVelocity * (1000 / 60);
canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
- // Draw alternate estimate.
+ // Draw velocity vector using an alternate VelocityTracker strategy.
if (mAltVelocity != null) {
- mPaint.setARGB(128, 0, 128, 128);
- lx = ps.mAltEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
- ly = ps.mAltEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
- for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
- float x = ps.mAltEstimator.estimateX(i * ESTIMATE_INTERVAL);
- float y = ps.mAltEstimator.estimateY(i * ESTIMATE_INTERVAL);
- canvas.drawLine(lx, ly, x, y, mPaint);
- lx = x;
- ly = y;
- }
-
mPaint.setARGB(255, 64, 255, 128);
xVel = ps.mAltXVelocity * (1000 / 60);
yVel = ps.mAltYVelocity * (1000 / 60);
diff --git a/core/java/com/android/server/AppWidgetBackupBridge.java b/core/java/com/android/server/AppWidgetBackupBridge.java
index 2ea2f79..7d82d35 100644
--- a/core/java/com/android/server/AppWidgetBackupBridge.java
+++ b/core/java/com/android/server/AppWidgetBackupBridge.java
@@ -16,6 +16,8 @@
package com.android.server;
+import android.annotation.Nullable;
+
import java.util.List;
/**
@@ -37,6 +39,8 @@
: null;
}
+ /** Returns a byte array of widget data for the specified package or {@code null}. */
+ @Nullable
public static byte[] getWidgetState(String packageName, int userId) {
return (sAppWidgetService != null)
? sAppWidgetService.getWidgetState(packageName, userId)
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 8083a6a..0a25271 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1198,6 +1198,10 @@
static jobject Bitmap_createHardwareBitmap(JNIEnv* env, jobject, jobject graphicBuffer) {
sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer));
+ // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
+ // format and SRGB color space.
+ // To support any color space, we need to pass an additional ColorSpace argument to
+ // java Bitmap.createHardwareBitmap.
sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
if (!bitmap.get()) {
ALOGW("failed to create hardware bitmap from graphic buffer");
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 3fcedd0..6ebf35c 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -22,8 +22,6 @@
#include "SkColorFilter.h"
#include "SkColorMatrixFilter.h"
-#include <Caches.h>
-
namespace android {
using namespace uirenderer;
diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp
index f8bb77a..755fcfb 100644
--- a/core/jni/android/graphics/Matrix.cpp
+++ b/core/jni/android/graphics/Matrix.cpp
@@ -20,7 +20,6 @@
#include "SkMatrix.h"
#include "core_jni_helpers.h"
-#include <Caches.h>
#include <jni.h>
namespace android {
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index cff7720..68f5bef 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -6,7 +6,6 @@
#include "SkBlendMode.h"
#include "core_jni_helpers.h"
-#include <Caches.h>
#include <jni.h>
using namespace android::uirenderer;
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index d098a35..3e464c6 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -36,6 +36,7 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
+#include "surfacetexture/SurfaceTexture.h"
// ----------------------------------------------------------------------------
@@ -80,10 +81,10 @@
// ----------------------------------------------------------------------------
static void SurfaceTexture_setSurfaceTexture(JNIEnv* env, jobject thiz,
- const sp<GLConsumer>& surfaceTexture)
+ const sp<SurfaceTexture>& surfaceTexture)
{
- GLConsumer* const p =
- (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture);
+ SurfaceTexture* const p =
+ (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture);
if (surfaceTexture.get()) {
surfaceTexture->incStrong((void*)SurfaceTexture_setSurfaceTexture);
}
@@ -108,10 +109,10 @@
}
static void SurfaceTexture_setFrameAvailableListener(JNIEnv* env,
- jobject thiz, sp<GLConsumer::FrameAvailableListener> listener)
+ jobject thiz, sp<SurfaceTexture::FrameAvailableListener> listener)
{
- GLConsumer::FrameAvailableListener* const p =
- (GLConsumer::FrameAvailableListener*)
+ SurfaceTexture::FrameAvailableListener* const p =
+ (SurfaceTexture::FrameAvailableListener*)
env->GetLongField(thiz, fields.frameAvailableListener);
if (listener.get()) {
listener->incStrong((void*)SurfaceTexture_setSurfaceTexture);
@@ -122,8 +123,8 @@
env->SetLongField(thiz, fields.frameAvailableListener, (jlong)listener.get());
}
-sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
- return (GLConsumer*)env->GetLongField(thiz, fields.surfaceTexture);
+sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz) {
+ return (SurfaceTexture*)env->GetLongField(thiz, fields.surfaceTexture);
}
sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz) {
@@ -131,7 +132,7 @@
}
sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz) {
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, thiz));
sp<Surface> surfaceTextureClient(surfaceTexture != NULL ? new Surface(producer) : NULL);
return surfaceTextureClient;
@@ -144,7 +145,7 @@
// ----------------------------------------------------------------------------
-class JNISurfaceTextureContext : public GLConsumer::FrameAvailableListener
+class JNISurfaceTextureContext : public SurfaceTexture::FrameAvailableListener
{
public:
JNISurfaceTextureContext(JNIEnv* env, jobject weakThiz, jclass clazz);
@@ -266,12 +267,12 @@
consumer->setMaxBufferCount(1);
}
- sp<GLConsumer> surfaceTexture;
+ sp<SurfaceTexture> surfaceTexture;
if (isDetached) {
- surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
+ surfaceTexture = new SurfaceTexture(consumer, GL_TEXTURE_EXTERNAL_OES,
true, !singleBufferMode);
} else {
- surfaceTexture = new GLConsumer(consumer, texName,
+ surfaceTexture = new SurfaceTexture(consumer, texName,
GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
}
@@ -306,7 +307,7 @@
static void SurfaceTexture_finalize(JNIEnv* env, jobject thiz)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
surfaceTexture->setFrameAvailableListener(0);
SurfaceTexture_setFrameAvailableListener(env, thiz, 0);
SurfaceTexture_setSurfaceTexture(env, thiz, 0);
@@ -315,13 +316,13 @@
static void SurfaceTexture_setDefaultBufferSize(
JNIEnv* env, jobject thiz, jint width, jint height) {
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
surfaceTexture->setDefaultBufferSize(width, height);
}
static void SurfaceTexture_updateTexImage(JNIEnv* env, jobject thiz)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
status_t err = surfaceTexture->updateTexImage();
if (err == INVALID_OPERATION) {
jniThrowException(env, IllegalStateException, "Unable to update texture contents (see "
@@ -333,7 +334,7 @@
static void SurfaceTexture_releaseTexImage(JNIEnv* env, jobject thiz)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
status_t err = surfaceTexture->releaseTexImage();
if (err == INVALID_OPERATION) {
jniThrowException(env, IllegalStateException, "Unable to release texture contents (see "
@@ -345,20 +346,20 @@
static jint SurfaceTexture_detachFromGLContext(JNIEnv* env, jobject thiz)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
return surfaceTexture->detachFromContext();
}
static jint SurfaceTexture_attachToGLContext(JNIEnv* env, jobject thiz, jint tex)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
return surfaceTexture->attachToContext((GLuint)tex);
}
static void SurfaceTexture_getTransformMatrix(JNIEnv* env, jobject thiz,
jfloatArray jmtx)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
float* mtx = env->GetFloatArrayElements(jmtx, NULL);
surfaceTexture->getTransformMatrix(mtx);
env->ReleaseFloatArrayElements(jmtx, mtx, 0);
@@ -366,19 +367,19 @@
static jlong SurfaceTexture_getTimestamp(JNIEnv* env, jobject thiz)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
return surfaceTexture->getTimestamp();
}
static void SurfaceTexture_release(JNIEnv* env, jobject thiz)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
surfaceTexture->abandon();
}
static jboolean SurfaceTexture_isReleased(JNIEnv* env, jobject thiz)
{
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
return surfaceTexture->isAbandoned();
}
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 23c3877..ab8e685 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -61,7 +61,7 @@
jstring uniqueId =
jstring(env->GetObjectField(viewportObj, gDisplayViewportClassInfo.uniqueId));
if (uniqueId != nullptr) {
- viewport->uniqueId.setTo(ScopedUtfChars(env, uniqueId).c_str());
+ viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str();
}
jobject logicalFrameObj =
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a72363c..d4a84c1 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -70,6 +70,9 @@
jfieldID mRole;
jfieldID mGains;
jfieldID mActiveConfig;
+ // Valid only if an AudioDevicePort
+ jfieldID mType;
+ jfieldID mAddress;
// other fields unused by JNI
} gAudioPortFields;
@@ -153,6 +156,9 @@
static struct {
jfieldID mUsage;
jfieldID mSource;
+ jfieldID mContentType;
+ jfieldID mFlags;
+ jfieldID mFormattedTags;
} gAudioAttributesFields;
static const char* const kEventHandlerClassPathName =
@@ -669,6 +675,26 @@
env->DeleteLocalRef(jValues);
}
+static jint convertAudioAttributesToNative(JNIEnv *env,
+ audio_attributes_t *nAudioAttributes,
+ const jobject jAudioAttributes)
+{
+ nAudioAttributes->usage = (audio_usage_t)env->GetIntField(jAudioAttributes,
+ gAudioAttributesFields.mUsage);
+ nAudioAttributes->source = (audio_source_t)env->GetIntField(jAudioAttributes,
+ gAudioAttributesFields.mSource);
+ nAudioAttributes->content_type = (audio_content_type_t)env->GetIntField(jAudioAttributes,
+ gAudioAttributesFields.mContentType);
+ nAudioAttributes->flags = env->GetIntField(jAudioAttributes,
+ gAudioAttributesFields.mFlags);
+ const jstring jtags = (jstring)env->GetObjectField(jAudioAttributes,
+ gAudioAttributesFields.mFormattedTags);
+ const char *tags = env->GetStringUTFChars(jtags, NULL);
+ strncpy(nAudioAttributes->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ env->ReleaseStringUTFChars(jtags, tags);
+ return (jint)AUDIO_JAVA_SUCCESS;
+}
+
static jint convertAudioPortConfigToNative(JNIEnv *env,
struct audio_port_config *nAudioPortConfig,
@@ -745,6 +771,42 @@
return (jint)AUDIO_JAVA_SUCCESS;
}
+/**
+ * Extends convertAudioPortConfigToNative with extra device port info.
+ * Mix / Session specific info is not fulfilled.
+ */
+static jint convertAudioPortConfigToNativeWithDevicePort(JNIEnv *env,
+ struct audio_port_config *nAudioPortConfig,
+ const jobject jAudioPortConfig,
+ bool useConfigMask)
+{
+ jint jStatus = convertAudioPortConfigToNative(env,
+ nAudioPortConfig,
+ jAudioPortConfig,
+ useConfigMask);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ // Supports AUDIO_PORT_TYPE_DEVICE only
+ if (nAudioPortConfig->type != AUDIO_PORT_TYPE_DEVICE) {
+ return (jint)AUDIO_JAVA_BAD_VALUE;
+ }
+
+ jobject jAudioDevicePort = env->GetObjectField(jAudioPortConfig,
+ gAudioPortConfigFields.mPort);
+ nAudioPortConfig->ext.device.type = env->GetIntField(jAudioDevicePort,
+ gAudioPortFields.mType);
+ jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioDevicePort,
+ gAudioPortFields.mAddress);
+ const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL);
+ strncpy(nAudioPortConfig->ext.device.address,
+ nDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN - 1);
+ env->ReleaseStringUTFChars(jDeviceAddress, nDeviceAddress);
+ env->DeleteLocalRef(jDeviceAddress);
+ env->DeleteLocalRef(jAudioDevicePort);
+ return jStatus;
+}
+
static jint convertAudioPortConfigFromNative(JNIEnv *env,
jobject jAudioPort,
jobject *jAudioPortConfig,
@@ -1592,6 +1654,51 @@
return jStatus;
}
+/**
+ * Returns handle if the audio source is successfully started.
+ */
+static jint
+android_media_AudioSystem_startAudioSource(JNIEnv *env, jobject clazz,
+ jobject jAudioPortConfig,
+ jobject jAudioAttributes)
+{
+ ALOGV("startAudioSource");
+ if (jAudioPortConfig == NULL || jAudioAttributes == NULL) {
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jAudioPortConfig, gAudioPortConfigClass)) {
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+ if (!env->IsInstanceOf(jAudioAttributes, gAudioAttributesClass)) {
+ return AUDIO_JAVA_BAD_VALUE;
+ }
+ struct audio_port_config nAudioPortConfig = {};
+ jint jStatus = convertAudioPortConfigToNativeWithDevicePort(env,
+ &nAudioPortConfig, jAudioPortConfig, false);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ audio_attributes_t nAudioAttributes = {};
+ jStatus = convertAudioAttributesToNative(env, &nAudioAttributes, jAudioAttributes);
+ if (jStatus != AUDIO_JAVA_SUCCESS) {
+ return jStatus;
+ }
+ audio_port_handle_t handle;
+ status_t status = AudioSystem::startAudioSource(&nAudioPortConfig, &nAudioAttributes, &handle);
+ ALOGV("AudioSystem::startAudioSource() returned %d handle %d", status, handle);
+ return handle > 0 ? handle : nativeToJavaStatus(status);
+}
+
+static jint
+android_media_AudioSystem_stopAudioSource(JNIEnv *env, jobject clazz, jint handle)
+{
+ ALOGV("stopAudioSource");
+ status_t status = AudioSystem::stopAudioSource(
+ static_cast <audio_port_handle_t>(handle));
+ ALOGV("AudioSystem::stopAudioSource() returned %d", status);
+ return nativeToJavaStatus(status);
+}
+
static void
android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this)
{
@@ -1947,6 +2054,9 @@
(void *)android_media_AudioSystem_listAudioPatches},
{"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
(void *)android_media_AudioSystem_setAudioPortConfig},
+ {"startAudioSource", "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
+ (void *)android_media_AudioSystem_startAudioSource},
+ {"stopAudioSource", "(I)I", (void *)android_media_AudioSystem_stopAudioSource},
{"getAudioHwSyncForSession", "(I)I",
(void *)android_media_AudioSystem_getAudioHwSyncForSession},
{"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
@@ -2040,6 +2150,11 @@
gAudioDevicePortCstor = GetMethodIDOrDie(env, audioDevicePortClass, "<init>",
"(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V");
+ // When access AudioPort as AudioDevicePort
+ gAudioPortFields.mType = GetFieldIDOrDie(env, audioDevicePortClass, "mType", "I");
+ gAudioPortFields.mAddress = GetFieldIDOrDie(env, audioDevicePortClass, "mAddress",
+ "Ljava/lang/String;");
+
jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort");
gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass);
gAudioMixPortCstor = GetMethodIDOrDie(env, audioMixPortClass, "<init>",
@@ -2120,6 +2235,11 @@
gAudioAttributesClass = MakeGlobalRefOrDie(env, audioAttributesClass);
gAudioAttributesFields.mUsage = GetFieldIDOrDie(env, audioAttributesClass, "mUsage", "I");
gAudioAttributesFields.mSource = GetFieldIDOrDie(env, audioAttributesClass, "mSource", "I");
+ gAudioAttributesFields.mContentType = GetFieldIDOrDie(env,
+ audioAttributesClass, "mContentType", "I");
+ gAudioAttributesFields.mFlags = GetFieldIDOrDie(env, audioAttributesClass, "mFlags", "I");
+ gAudioAttributesFields.mFormattedTags = GetFieldIDOrDie(env,
+ audioAttributesClass, "mFormattedTags", "Ljava/lang/String;");
AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback);
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 494fad7..a698d66 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -37,13 +37,13 @@
} gInputDeviceClassInfo;
jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) {
- ScopedLocalRef<jstring> nameObj(env, env->NewStringUTF(deviceInfo.getDisplayName().string()));
+ ScopedLocalRef<jstring> nameObj(env, env->NewStringUTF(deviceInfo.getDisplayName().c_str()));
if (!nameObj.get()) {
return NULL;
}
ScopedLocalRef<jstring> descriptorObj(env,
- env->NewStringUTF(deviceInfo.getIdentifier().descriptor.string()));
+ env->NewStringUTF(deviceInfo.getIdentifier().descriptor.c_str()));
if (!descriptorObj.get()) {
return NULL;
}
diff --git a/core/jni/android_view_TextureLayer.cpp b/core/jni/android_view_TextureLayer.cpp
index d3a447f..1ccb6a8 100644
--- a/core/jni/android_view_TextureLayer.cpp
+++ b/core/jni/android_view_TextureLayer.cpp
@@ -67,8 +67,7 @@
static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz,
jlong layerUpdaterPtr, jobject surface) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
- sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
- layer->setSurfaceTexture(surfaceTexture);
+ layer->setSurfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
}
static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz,
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 4a17742..3c59bd1 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1024,6 +1024,9 @@
// Continue I guess?
}
sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer);
+ // Bitmap::createFrom currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888
+ // format and SRGB color space.
+ // To support any color space, we could extract it from BufferItem and pass it to Bitmap.
return bitmap::createBitmap(env, bitmap.release(),
android::bitmap::kBitmapCreateFlag_Premultiplied);
}
diff --git a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h
index c534d4b..0ad2587 100644
--- a/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h
+++ b/core/jni/include/android_runtime/android_graphics_SurfaceTexture.h
@@ -23,14 +23,14 @@
namespace android {
-class GLConsumer;
class IGraphicBufferProducer;
+class SurfaceTexture;
extern sp<ANativeWindow> android_SurfaceTexture_getNativeWindow(JNIEnv* env, jobject thiz);
extern bool android_SurfaceTexture_isInstanceOf(JNIEnv* env, jobject thiz);
-/* Gets the underlying GLConsumer from a SurfaceTexture Java object. */
-extern sp<GLConsumer> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz);
+/* Gets the underlying C++ SurfaceTexture object from a SurfaceTexture Java object. */
+extern sp<SurfaceTexture> SurfaceTexture_getSurfaceTexture(JNIEnv* env, jobject thiz);
/* gets the producer end of the SurfaceTexture */
extern sp<IGraphicBufferProducer> SurfaceTexture_getProducer(JNIEnv* env, jobject thiz);
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index f1bd63b..1257336 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -271,7 +271,7 @@
optional .android.graphics.RectProto containing_frame = 8 [deprecated=true];
optional .android.graphics.RectProto parent_frame = 9 [deprecated=true];
optional .android.graphics.RectProto content_frame = 10 [deprecated=true];
- optional .android.graphics.RectProto content_insets = 11;
+ optional .android.graphics.RectProto content_insets = 11 [deprecated=true];
optional .android.graphics.RectProto surface_insets = 12;
optional WindowStateAnimatorProto animator = 13;
optional bool animating_exit = 14;
@@ -288,10 +288,10 @@
optional .android.graphics.RectProto visible_frame = 26 [deprecated=true];
optional .android.graphics.RectProto decor_frame = 27 [deprecated=true];
optional .android.graphics.RectProto outset_frame = 28 [deprecated=true];
- optional .android.graphics.RectProto overscan_insets = 29;
- optional .android.graphics.RectProto visible_insets = 30;
- optional .android.graphics.RectProto stable_insets = 31;
- optional .android.graphics.RectProto outsets = 32;
+ optional .android.graphics.RectProto overscan_insets = 29 [deprecated=true];
+ optional .android.graphics.RectProto visible_insets = 30 [deprecated=true];
+ optional .android.graphics.RectProto stable_insets = 31 [deprecated=true];
+ optional .android.graphics.RectProto outsets = 32 [deprecated=true];
optional .android.view.DisplayCutoutProto cutout = 33 [deprecated=true];
optional bool remove_on_exit = 34;
optional bool destroying = 35;
@@ -380,4 +380,9 @@
optional .android.graphics.RectProto parent_frame = 8;
optional .android.graphics.RectProto visible_frame = 9;
optional .android.view.DisplayCutoutProto cutout = 10;
+ optional .android.graphics.RectProto content_insets = 11;
+ optional .android.graphics.RectProto overscan_insets = 12;
+ optional .android.graphics.RectProto visible_insets = 13;
+ optional .android.graphics.RectProto stable_insets = 14;
+ optional .android.graphics.RectProto outsets = 15;
}
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index 736871f..ce19ce3 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -21,6 +21,7 @@
option java_outer_classname = "ProcessStatsServiceProto";
import "frameworks/base/core/proto/android/util/common.proto";
+import "frameworks/base/core/proto/android/service/procstats_enum.proto";
import "frameworks/base/libs/incident/proto/android/privacy.proto";
/**
@@ -86,55 +87,12 @@
message ProcessStatsStateProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
- enum ScreenState {
- SCREEN_UNKNOWN = 0;
- OFF = 1;
- ON = 2;
- }
optional ScreenState screen_state = 1;
- enum MemoryState {
- MEMORY_UNKNOWN = 0;
- NORMAL = 1; // normal.
- MODERATE = 2; // moderate memory pressure.
- LOW = 3; // low memory.
- CRITICAL = 4; // critical memory.
- }
optional MemoryState memory_state = 2;
// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
// and not frameworks/base/core/java/android/app/ActivityManager.java
- enum ProcessState {
- PROCESS_UNKNOWN = 0;
- // Persistent system process.
- PERSISTENT = 1;
- // Top activity; actually any visible activity.
- TOP = 2;
- // Important foreground process (ime, wallpaper, etc).
- IMPORTANT_FOREGROUND = 3;
- // Important background process.
- IMPORTANT_BACKGROUND = 4;
- // Performing backup operation.
- BACKUP = 5;
- // Background process running a service.
- SERVICE = 6;
- // Process not running, but would be if there was enough RAM.
- SERVICE_RESTARTING = 7;
- // Process running a receiver.
- RECEIVER = 8;
- // Heavy-weight process (currently not used).
- HEAVY_WEIGHT = 9;
- // Process hosting home/launcher app when not on top.
- HOME = 10;
- // Process hosting the last app the user was in.
- LAST_ACTIVITY = 11;
- // Cached process hosting a previous activity.
- CACHED_ACTIVITY = 12;
- // Cached process hosting a client activity.
- CACHED_ACTIVITY_CLIENT = 13;
- // Cached process that is empty.
- CACHED_EMPTY = 14;
- }
optional ProcessState process_state = 3;
// Millisecond uptime duration spent in this state
@@ -189,74 +147,85 @@
optional ProcessStatsStateProto total_running_state = 6;
}
-// Next Tag: 7
-message ProcessStatsServiceStateProto {
+// Next Tag: 4
+message PackageServiceOperationStatsProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Operate enum: Started, Foreground, Bound, Executing
+ optional ServiceOperationState operation = 1;
+
+ // Number of times the service was in this operation.
+ optional int32 count = 2;
+
+ // Information about a state the service can be in.
+ message StateStats {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Screen state enum.
+ optional android.service.procstats.ScreenState screen_state = 1;
+ // Memory state enum.
+ optional android.service.procstats.MemoryState memory_state = 2;
+
+ // duration in milliseconds.
+ optional int64 duration_ms = 3;
+ // Millisecond elapsed realtime duration spent in this state
+ optional int64 realtime_duration_ms = 4;
+ }
+ repeated StateStats state_stats = 3;
+}
+
+// Next Tag: 3
+message PackageServiceStatsProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// Name of service component.
optional string service_name = 1;
- // Information about a state the service can be in.
- message OperationInfo {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // Number of times the service was in this operation.
- optional int32 count = 1;
-
- // Time this operation was active in various states. process_state will not be set;
- // includes only uptime, not memory info.
- repeated ProcessStatsStateProto states = 2;
- }
-
- // Information about when the service was operating as running (that is how long it was
- // running for any reason, such as the finer-grained started, bound, and executing times
- // also reported in this proto).
- optional OperationInfo running_op = 2;
-
- // Information about when the service was operating as started.
- optional OperationInfo started_op = 3;
-
- // Information about when the service was operating as foreground.
- optional OperationInfo foreground_op = 4;
-
- // Information about when the service was operating as bound.
- optional OperationInfo bound_op = 5;
-
- // Information about when the service was operating as executing.
- optional OperationInfo executing_op = 6;
+ // The operation stats.
+ // The package_name, package_uid, package_version, service_name will not be set to save space.
+ repeated PackageServiceOperationStatsProto operation_stats = 2;
}
// Next Tag: 7
-message ProcessStatsAssociationStateProto {
+message PackageAssociationSourceProcessStatsProto {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Uid of the process.
+ optional int32 process_uid = 1;
+ // Process name.
+ optional string process_name = 2;
+
+ // Total count of the times this association appeared.
+ optional int32 total_count = 3;
+
+ // Millisecond uptime total duration this association was around.
+ optional int64 total_duration_ms = 4;
+
+ // Total count of the times this association became actively impacting its target process.
+ optional int32 active_count = 5;
+
+ // Information on one source in this association.
+ message StateStats {
+ option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ // Process state enum.
+ optional android.service.procstats.ProcessState process_state = 1;
+ // Millisecond uptime duration spent in this state
+ optional int64 duration_ms = 2;
+ // Millisecond elapsed realtime duration spent in this state
+ optional int64 realtime_duration_ms = 3;
+ }
+ repeated StateStats active_state_stats = 6;
+}
+
+// Next Tag: 3
+message PackageAssociationProcessStatsProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// Name of the target component.
optional string component_name = 1;
-
// Information on one source in this association.
- message Source {
- option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // Name of source process.
- optional string process = 1;
-
- // Uid of the source process.
- optional int32 uid = 2;
-
- // Total count of the times this association appeared.
- optional int32 total_count = 3;
-
- // Millisecond uptime total duration this association was around.
- optional int64 total_duration_ms = 4;
-
- // Total count of the times this association became actively impacting its target process.
- optional int32 active_count = 5;
-
- // Time association was active in various states. screen_state and memory_state will not
- // be set; includes only uptime, not memory info.
- repeated ProcessStatsStateProto active_states = 6;
- }
- repeated Source sources = 3;
+ repeated PackageAssociationSourceProcessStatsProto sources = 2;
}
// Next Tag: 7
@@ -276,8 +245,8 @@
repeated ProcessStatsProto process_stats = 4;
// Stats for each of the package's services.
- repeated ProcessStatsServiceStateProto service_stats = 5;
+ repeated PackageServiceStatsProto service_stats = 5;
// Stats for each association with the package.
- repeated ProcessStatsAssociationStateProto association_stats = 6;
+ repeated PackageAssociationProcessStatsProto association_stats = 6;
}
diff --git a/core/proto/android/service/procstats_enum.proto b/core/proto/android/service/procstats_enum.proto
new file mode 100644
index 0000000..cc3fe5a
--- /dev/null
+++ b/core/proto/android/service/procstats_enum.proto
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.service.procstats;
+
+option java_multiple_files = true;
+option java_outer_classname = "ProcessStatsEnums";
+
+enum ScreenState {
+ SCREEN_STATE_UNKNOWN = 0;
+ SCREEN_STATE_OFF = 1;
+ SCREEN_STATE_ON = 2;
+}
+
+enum MemoryState {
+ MEMORY_STATE_UNKNOWN = 0;
+ MEMORY_STATE_NORMAL = 1; // normal.
+ MEMORY_STATE_MODERATE = 2; // moderate memory pressure.
+ MEMORY_STATE_LOW = 3; // low memory.
+ MEMORY_STATE_CRITICAL = 4; // critical memory.
+}
+
+// this enum list is from frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
+// and not frameworks/base/core/java/android/app/ActivityManager.java
+enum ProcessState {
+ PROCESS_STATE_UNKNOWN = 0;
+ // Persistent system process.
+ PROCESS_STATE_PERSISTENT = 1;
+ // Top activity; actually any visible activity.
+ PROCESS_STATE_TOP = 2;
+ // Important foreground process (ime, wallpaper, etc).
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 3;
+ // Important background process.
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
+ // Performing backup operation.
+ PROCESS_STATE_BACKUP = 5;
+ // Background process running a service.
+ PROCESS_STATE_SERVICE = 6;
+ // Process not running, but would be if there was enough RAM.
+ PROCESS_STATE_SERVICE_RESTARTING = 7;
+ // Process running a receiver.
+ PROCESS_STATE_RECEIVER = 8;
+ // Heavy-weight process (currently not used).
+ PROCESS_STATE_HEAVY_WEIGHT = 9;
+ // Process hosting home/launcher app when not on top.
+ PROCESS_STATE_HOME = 10;
+ // Process hosting the last app the user was in.
+ PROCESS_STATE_LAST_ACTIVITY = 11;
+ // Cached process hosting a previous activity.
+ PROCESS_STATE_CACHED_ACTIVITY = 12;
+ // Cached process hosting a client activity.
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 13;
+ // Cached process that is empty.
+ PROCESS_STATE_CACHED_EMPTY = 14;
+}
+
+enum ServiceOperationState {
+ SERVICE_OPERATION_STATE_UNKNOWN = 0;
+ SERVICE_OPERATION_STATE_RUNNING = 1;
+ SERVICE_OPERATION_STATE_STARTED = 2;
+ SERVICE_OPERATION_STATE_FOREGROUND = 3;
+ SERVICE_OPERATION_STATE_BOUND = 4;
+ SERVICE_OPERATION_STATE_EXECUTING = 5;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5258518..f845bd0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3345,7 +3345,7 @@
@hide
@removed -->
<permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged" />
<!-- Allows an application to capture secure video output.
<p>Not for use by third-party applications.</p>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index e136d2e..c2c9254 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Laat die program toe om jou fotoversameling te wysig."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"lees liggings in jou mediaversameling"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Laat die program toe om liggings in jou mediaversameling te lees."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometriese hardeware is nie beskikbaar nie"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelike vingerafdruk is bespeur. Probeer asseblief weer."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kon nie vingerafdruk verwerk nie. Probeer asseblief weer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Vingerafdruksensor is vuil. Maak dit skoon en probeer weer."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vinger is te stadig beweer. Probeer asseblief weer."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nie herken nie"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nie herken nie"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Vingerafdruk is gestaaf"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Vingerafdrukhardeware is nie beskikbaar nie."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Vingerafdruk kan nie gestoor word nie. Verwyder asseblief \'n bestaande vingerafdruk."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 5a7e643..45f962b 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"መተግበሪያው የፎቶ ስብስብዎን እንዲቀይረው ያስችለዋል።"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"አካባቢዎችን ከሚዲያ ስብስብዎ ማንበብ"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"መተግበሪያው አካባቢዎችን ከሚዲያ ስብስብዎ እንዲያነብብ ያስችለዋል።"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ባዮሜትራዊ ሃርድዌር አይገኝም"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ከፊል የጣት አሻራ ተገኝቷል። እባክዎ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ጣት አሻራን መስራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"የጣት አሻራ ዳሳሽ ቆሽሿል። እባክዎ ያጽዱት እና እንደገና ይሞክሩ።"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ጣት ከልክ በላይ ተንቀራፎ ተንቀሳቅሷል። እባክዎ እንደገና ይሞክሩ።"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"አልታወቀም"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"አልታወቀም"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"የጣት አሻራ ትክክለኛነት ተረጋግጧል"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"የጣት አሻራ ሃርድዌር አይገኝም።"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"የጣት አሻራ ሊከማች አይችልም። እባክዎ አሁን ያለውን የጣት አሻራ ያስወግዱ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index d46f758..bbdea6a 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -526,6 +526,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"للسماح للتطبيق بتعديل مجموعة صورك."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"قراءة المواقع من مجموعة الوسائط التابعة لك"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"للسماح للتطبيق بقراءة المواقع من مجموعة الوسائط التابعة لك."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"معدّات المقاييس الحيوية غير متاحة."</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"تم اكتشاف بصمة الإصبع بشكل جزئي؛ يرجى إعادة المحاولة."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"تعذرت معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"جهاز استشعار بصمات الأصابع متسخ، يرجى تنظيفه وإعادة المحاولة."</string>
@@ -533,7 +534,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"تم تحريك الإصبع ببطء شديد. يُرجى إعادة المحاولة."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"لم يتم التعرف عليها"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"لم يتم التعرف عليها."</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"تم مصادقة بصمة الإصبع"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"جهاز بصمة الإصبع غير متاح."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"يتعذر تخزين بصمة الإصبع؛ يرجى إزالة إحدى البصمات المخزنة."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 10b7da43..48d44b0 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"এপক আপোনাৰ ফট’ সংগ্ৰহ সালসলনি কৰিবলৈ দিয়ে।"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"এপক আপোনাৰ মিডিয়া সংগ্ৰহৰ অৱস্থান পঢ়িবলৈ দিয়ে।"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"বায়োমেট্ৰিক হাৰ্ডৱেৰ উপলব্ধ নহয়"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ফিংগাৰপ্ৰিণ্ট আংশিকভাৱে চিনাক্ত কৰা হৈছে। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ফিগাৰপ্ৰিণ্টৰ প্ৰক্ৰিয়া সম্পাদন কৰিবপৰা নগ\'ল। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো লেতেৰা হৈ আছে। অনুগ্ৰহ কৰি পৰিষ্কাৰ কৰি আকৌ চেষ্টা কৰক।"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"আঙুলিৰ গতি অতি মন্থৰ আছিল। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"চিনাক্ত কৰিবপৰা নগ\'ল"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"চিনাক্ত কৰিব পৰা নাই"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ফিংগাৰপ্ৰিণ্টৰ সত্যাপন কৰা হ’ল"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ফিংগাৰপ্ৰিণ্ট সঞ্চয় কৰিব পৰা নগ\'ল। পূর্বে সঞ্চিত ফিংগাৰপ্ৰিণ্ট এটা আঁতৰাওক।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 6464c61..44bcd56 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Tətbiqin foto kolleksiyanıza düzəliş etməsinə icazə verir."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"media kolleksiyanızdan məkanları oxuyun"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tətbiqin media kolleksiyanızdan məkanları oxumasına icazə verin."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrik proqram əlçatan deyil"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmaq qismən müəyyən olundu. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmaq izi tanınmadı. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Barmaq izi sensoru çirklidir. Lütfən, təmizləyin və yenidən cəhd edin."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Barmağınızı çox yavaş hərəkət etdirdiniz. Lütfən, yenidən cəhd edin."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Tanınmır"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmır"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Barmaq izi doğrulandı"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Barmaq izi üçün avadanlıq yoxdur."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Barmaq izi saxlana bilməz. Lütfən, mövcud barmaq izini silin."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 4d31b1b..260f63e 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -517,6 +517,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Dozvoljava aplikaciji da menja kolekciju slika."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"čitanje lokacija iz medijske kolekcije"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Dozvoljava aplikaciji da čita lokacije iz medijske kolekcije."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je delimični otisak prsta. Probajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nije uspela obrada otiska prsta. Probajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor za otiske prstiju je prljav. Očistite ga i pokušajte ponovo."</string>
@@ -524,7 +525,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Previše sporo ste pomerili prst. Probajte ponovo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nije prepoznat"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Otisak prsta je potvrđen"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otiske prstiju nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Nije moguće sačuvati otisak prsta. Uklonite neki od postojećih otisaka prstiju."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 77223d7..3d46662 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Праграма зможа змяняць фотакалекцыю."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"паказваць месцазнаходжанне ў калекцыі мультымедыя"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Праграма зможа паказваць месцазнаходжанне ў калекцыі мультымедыя."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Біяметрычнае абсталяванне недаступнае"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Выяўлена частка адбіткаў пальцаў. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не атрымалася апрацаваць адбітак пальца. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчык адбіткаў пальцаў брудны. Ачысціце яго і паспрабуйце яшчэ раз."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Палец рухаўся занадта павольна. Паспрабуйце яшчэ раз."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Не распазнаны"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Не распазнана"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Адбітак пальца распазнаны"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Апаратныя сродкі адбіткаў пальцаў недаступныя."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Адбіткі пальцаў нельга захаваць. Выдаліце існы адбітак."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 3fc31f0..e9ecba6 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Разрешава на приложението да променя колекцията ви от снимки."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"да чете местоположенията от мултимедийната ви колекция"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Разрешава на приложението да чете местоположенията от мултимедийната ви колекция."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометричният хардуер не е налице"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Открит е частичен отпечатък. Моля, опитайте отново."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатъкът не можа да се обработи. Моля, опитайте отново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорът за отпечатъци е мръсен. Моля, почистете го и опитайте отново."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Преместихте пръста си твърде бавно. Моля, опитайте отново."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Не е разпознато"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Не е разпознато"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечатъкът е удостоверен"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хардуерът за отпечатъци не е налице."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатъкът не може да бъде съхранен. Моля, премахнете съществуващ."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index e7990ca..4ece21b 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"অ্যাপকে আপনার ফটো সংগ্রহ পরিবর্তন করার অনুমতি দিন।"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ডিয়া সংগ্রহ থেকে লোকেশন দেখতে দিন"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"আপনার মিডিয়া সংগ্রহ থেকে লোকেশন দেখতে অ্যাপকে অনুমতি দিন।"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"বায়োমেট্রিক হার্ডওয়্যার পাওয়া যাবে না"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"আঙ্গুলের ছাপ আংশিক শনাক্ত করা হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"আঙ্গুলের ছাপ প্রক্রিয়া করা যায়নি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"আঙ্গুলের ছাপ নেওয়ার সেন্সরটি অপরিস্কার৷ অনুগ্রহ করে পরিষ্কার করে আবার চেষ্টা করুন৷"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"আঙ্গুল খুব ধীরে সরানো হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"স্বীকৃত নয়"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"স্বীকৃত নয়"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"আঙ্গুলের ছাপ যাচাই করা হয়েছে"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার অনুপলব্ধ৷"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"আঙ্গুলের ছাপ সংরক্ষণ করা যাবে না৷ অনুগ্রহ করে একটি বিদ্যমান আঙ্গুলের ছাপ সরান৷"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 244af73..918ecf9 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -517,6 +517,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Omogućava aplikaciji da mijenja vašu kolekciju fotografija."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"čitanje lokacija iz kolekcije medija"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Omogućava aplikaciji da čita lokacije iz vaše kolekcije medija."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je djelomičan otisak prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nije uspjela obrada otiska prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor za otisak prsta je prljav. Očistite ga i pokušajte ponovo."</string>
@@ -524,7 +525,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Prst je uklonjen presporo. Pokušajte ponovo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nije prepoznato"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Otisak prsta je potvrđen"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otisak prsta nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta se ne može pohraniti. Uklonite postojeći otisak prsta."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 2c74e82..f6c46ba 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -216,7 +216,7 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"Opcions del telèfon"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Bloqueig de pantalla"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Apaga"</string>
- <string name="global_action_emergency" msgid="7112311161137421166">"Emergències"</string>
+ <string name="global_action_emergency" msgid="7112311161137421166">"Emergència"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Informe d\'error"</string>
<string name="global_action_logout" msgid="935179188218826050">"Finalitza la sessió"</string>
<string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permet que l\'aplicació modifiqui la teva col·lecció de fotos."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"llegir les ubicacions de les teves col·leccions multimèdia"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permet que l\'aplicació llegeixi les ubicacions de les teves col·leccions multimèdia."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Maquinari biomètric no disponible"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta digital parcial. Torna-ho a provar."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor d\'empremtes digitals està brut. Neteja\'l i torna-ho a provar."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"El dit s\'ha mogut massa lentament. Torna-ho a provar."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"No s\'ha reconegut"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"No s\'ha reconegut"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"L\'empremta digital s\'ha autenticat"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El maquinari per a empremtes digitals no està disponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empremta digital no es pot desar. Suprimeix-ne una."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 278ab957..e882f42 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Umožňuje aplikaci upravit vaši sbírku fotek."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"čtení míst ze sbírky médií"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Umožňuje aplikaci číst místa z vaší sbírky médií."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrický hardware není k dispozici"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Byla zjištěna jen část otisku prstu. Zkuste to znovu."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Zpracování otisku prstu se nezdařilo. Zkuste to znovu."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor otisků prstů je znečištěn. Vyčistěte jej a zkuste to znovu."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Pohyb prstem byl příliš pomalý. Zkuste to znovu."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nerozpoznáno"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nerozpoznáno"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Otisk byl ověřen"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Není k dispozici hardware ke snímání otisků prstů."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisk prstu nelze uložit. Odstraňte existující otisk prstu."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 32be0d3..9a793a87 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -383,13 +383,13 @@
<string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Giver appen lov til at sende klæbende udsendelser, som ikke forsvinder, når udsendelsen er slut. Overdreven brug kan gøre fjernsynet langsomt eller ustabilt ved at få det til at bruge for meget hukommelse."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Tillader, at appen kan sende klæbende udsendelser, der forbliver tilbage, når udsendelsen er slut. Overdreven brug kan gøre din telefon langsom eller ustabil ved at tvinge den til at bruge for meget hukommelse."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"læse dine kontaktpersoner"</string>
- <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillader, at appen kan læse data om de kontaktpersoner, der er gemt på din tablet, f.eks. hvor ofte du har ringet til, sendt mail til eller på anden måde kommunikeret med bestemte personer. Med denne tilladelse kan apps gemme dine kontaktdata, og skadelige apps kan dele kontaktdata uden din viden."</string>
- <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Giver appen lov til at læse data om dine kontaktpersoner, der er gemt på dit tv, herunder hvor ofte du har ringet, mailet eller på andre måder kommunikeret med bestemte personer. Denne tilladelse gør det muligt for apps at gemme dine kontaktoplysninger, og ondsindede apps kan dele kontaktoplysninger uden din viden."</string>
- <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Tillader, at appen kan læse data om de kontaktpersoner, der er gemt på din telefon, f.eks. hvor ofte du har ringet til, sendt mail til eller på anden måde kommunikeret med bestemte personer. Med denne tilladelse kan apps gemme dine kontaktdata, og skadelige apps kan dele kontaktdata uden din viden."</string>
+ <string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillader, at appen kan læse data om de kontakter, der er gemt på din tablet, f.eks. hvor ofte du har ringet til, sendt mail til eller på anden måde kommunikeret med bestemte personer. Med denne tilladelse kan apps gemme dine kontaktdata, og skadelige apps kan dele kontaktdata uden din viden."</string>
+ <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Giver appen lov til at læse data om dine kontakter, der er gemt på dit tv, herunder hvor ofte du har ringet, mailet eller på andre måder kommunikeret med bestemte personer. Denne tilladelse gør det muligt for apps at gemme dine kontaktoplysninger, og ondsindede apps kan dele kontaktoplysninger uden din viden."</string>
+ <string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Tillader, at appen kan læse data om de kontakter, der er gemt på din telefon, f.eks. hvor ofte du har ringet til, sendt mail til eller på anden måde kommunikeret med bestemte personer. Med denne tilladelse kan apps gemme dine kontaktdata, og skadelige apps kan dele kontaktdata uden din viden."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"ændre dine kontaktpersoner"</string>
- <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt på din tablet, f.eks. hvor ofte du har ringet til dem, sendt dem en mail eller på anden måde kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Giver appen lov til at ændre data om dine kontaktpersoner, der er gemt på dit tv, herunder hvor ofte du har ringet, mailet eller på anden måde kommunikeret med bestemte kontaktpersoner. Denne tilladelse gør det muligt for apps at slette kontaktoplysninger."</string>
- <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillader, at appen kan ændre data om de kontaktpersoner, der er gemt på din telefon, f.eks. hvor ofte du har ringet til dem, sendt en mail til dem eller på anden måde kommunikeret med bestemte kontaktpersoner. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
+ <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillader, at appen kan ændre data om de kontakter, der er gemt på din tablet, f.eks. hvor ofte du har ringet til dem, sendt dem en mail eller på anden måde kommunikeret med bestemte kontakter. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Giver appen lov til at ændre data om dine kontakter, der er gemt på dit tv, herunder hvor ofte du har ringet, mailet eller på anden måde kommunikeret med bestemte kontakter. Denne tilladelse gør det muligt for apps at slette kontaktoplysninger."</string>
+ <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillader, at appen kan ændre data om de kontakter, der er gemt på din telefon, f.eks. hvor ofte du har ringet til dem, sendt en mail til dem eller på anden måde kommunikeret med bestemte kontakter. Med denne tilladelse kan apps slette kontaktoplysninger."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"læse opkaldsliste"</string>
<string name="permdesc_readCallLog" msgid="3204122446463552146">"Denne app kan læse din opkaldshistorik."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv opkaldsliste"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Tillader, at appen kan ændre din billedsamling."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"læse placeringer fra din mediesamling"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tillader, at appen kan læse placeringer fra din mediesamling."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk hardware er ikke tilgængelig"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Der blev registreret et delvist fingeraftryk. Prøv igen."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensoren til registrering af fingeraftryk er beskidt. Tør den af, og prøv igen."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Du bevægede fingeren for langsomt. Prøv igen."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ikke genkendt"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ikke genkendt"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeraftrykket blev godkendt"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardwaren til fingeraftryk er ikke tilgængelig."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeraftrykket kan ikke gemmes. Fjern et eksisterende fingeraftryk."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index e5b9372..8eb0eb8 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Ermöglicht der App, deine Fotosammlung zu ändern."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"Standorte aus meiner Mediensammlung abrufen"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ermöglicht der App, Standorte aus deiner Mediensammlung abzurufen."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrische Hardware nicht verfügbar"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Versuche es erneut."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Versuche es erneut."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerabdrucksensor ist verschmutzt. Reinige ihn und versuche es erneut."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger zu langsam bewegt. Versuche es erneut."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nicht erkannt"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nicht erkannt"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerabdruck wurde authentifiziert"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerabdruckhardware nicht verfügbar"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerabdruck kann nicht gespeichert werden. Entferne einen vorhandenen Fingerabdruck."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 482e29f..3a50f9e 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Επιτρέπει στην εφαρμογή να τροποποιήσει τη συλλογή φωτογραφιών σας."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ανάγνωση τοποθεσιών από τη συλλογή πολυμέσων σας"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Επιτρέπει στην εφαρμογή να διαβάσει τοποθεσίες από τη συλλογή πολυμέσων σας."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Δεν υπάρχει διαθέσιμος βιομετρικός εξοπλισμός"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Εντοπίστηκε μερικό μοναδικό χαρακτηριστικό. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Δεν ήταν δυνατή η επεξεργασία του μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Ο αισθητήρας μοναδικού χαρακτηριστικού δεν είναι καθαρός. Καθαρίστε τον και δοκιμάστε ξανά."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Πολύ αργή κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Δεν αναγνωρίστηκε"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Δεν αναγνωρίστηκε"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Η ταυτότητα του δακτυλικού αποτυπώματος ελέγχθηκε"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Ο εξοπλισμός μοναδικού χαρακτηριστικού δεν είναι διαθέσιμος."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Δεν είναι δυνατή η αποθήκευση μοναδικού χαρακτηριστικού. Καταργήστε το υπάρχον μοναδικό χαρακτηριστικό."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 19d0916..04b20ba 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Not recognised"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 1e2e081..c1ca0c4 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Not recognised"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 19d0916..04b20ba 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Not recognised"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 19d0916..04b20ba 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Not recognised"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognised"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 68bc261..4add775 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Allows the app to modify your photo collection."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"read locations from your media collection"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Allows the app to read locations from your media collection."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometric hardware unavailable"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Finger moved too slow. Please try again."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Not recognized"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Not recognized"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingerprint authenticated"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 9969af3..f317a4e 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que la app modifique tu colección de fotos."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"leer ubicaciones de tu colección de contenido multimedia"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que la app lea las ubicaciones de tu colección de contenido multimedia."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"No hay hardware biométrico disponible"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"La huella digital se detectó parcialmente. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se pudo procesar la huella digital. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor de huellas digitales está sucio. Limpia el sensor y vuelve a intentarlo."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Moviste el dedo muy lento. Vuelve a intentarlo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"No reconocido"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"No se reconoció"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Se autenticó la huella digital"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El hardware para detectar huellas digitales no está disponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"No se puede almacenar la huella digital. Elimina una de las existentes."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index e40a546..c42f53c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que la aplicación modifique tu colección de fotos."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"leer las ubicaciones de tu colección de contenido multimedia"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que la aplicación lea las ubicaciones de tu colección de contenido multimedia."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico no disponible"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Se ha detectado una huella digital parcial. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se ha podido procesar la huella digital. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor de huellas digitales está sucio. Límpialo y vuelve a intentarlo."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Has movido el dedo demasiado despacio. Vuelve a intentarlo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"No reconocido"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"No se reconoce"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Se ha autenticado la huella digital"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"El hardware de huella digital no está disponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"No se puede almacenar la huella digital. Elimina una ya creada."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 56181c8..cc052c4 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Võimaldab rakendusel muuta teie fotokogu."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"Lugeda teie meediakogus olevaid asukohti"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Võimaldab rakendusel lugeda teie meediakogus olevaid asukohti."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biomeetriline riistvara ei ole saadaval"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Tuvastati osaline sõrmejälg. Proovige uuesti."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sõrmejälge ei õnnestunud töödelda. Proovige uuesti."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sõrmejäljeandur on must. Puhastage see ja proovige uuesti."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Sõrm liikus liiga aeglaselt. Proovige uuesti."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ei tuvastatud"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ei tuvastatud"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Sõrmejälg autenditi"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Sõrmejälje riistvara pole saadaval."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sõrmejälge ei saa salvestada. Eemaldage olemasolev sõrmejälg."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 5ec5637..275e171 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Argazki-bilduma aldatzea baimentzen die aplikazioei."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"multimedia-edukien bildumako kokapena irakurri"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Multimedia-edukien bildumako kokapena irakurtzea baimentzen die aplikazioei."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrikoa ez dago erabilgarri"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hatz-marka digitala ez da osorik hauteman. Saiatu berriro."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ezin izan da prozesatu hatz-marka. Saiatu berriro."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Hatz-marka digitalen sentsorea zikina dago. Garbi ezazu, eta saiatu berriro."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Mantsoegi mugitu duzu hatza. Saiatu berriro."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ez da ezagutzen"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ez da ezagutu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Autentifikatu da hatz-marka"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hatz-markaren hardwarea ez dago erabilgarri."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Ezin da gorde hatz-marka digitala. Kendu lehendik gordeta duzunetako bat."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b64937c..5055e8b 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"به برنامه اجازه میدهد مجموعه عکستان را تغییر دهد."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"خواندن مکانها از مجموعه رسانه شما"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"به برنامه اجازه میدهد مکانها را از مجموعه رسانهتان بخواند."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"سختافزار زیستسنجی دردسترس نیست"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"حسگر اثر انگشت کثیف است. لطفاً آن را تمیز کنید و دوباره امتحان نمایید."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"حرکت انگشت خیلی آهسته بود. لطفاً دوباره امتحان کنید."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"شناخته نشد"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"شناسایی نشد"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"اثر انگشت احراز هویت شد"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"سختافزار اثرانگشت در دسترس نیست."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ذخیره اثر انگشت ممکن نیست. لطفاً یک اثر انگشت موجود را حذف کنید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index e13a01f..d54cb72 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Antaa sovelluksen muokata kuvakokoelmaasi."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"lukea mediakokoelmasi sijainteja"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Antaa sovelluksen lukea mediakokoelmasi sijainteja."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrinen laitteisto ei käytettävissä"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sormenjälki havaittiin vain osittain. Yritä uudelleen."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sormenjäljen käsittely epäonnistui. Yritä uudelleen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sormenjälkitunnistin on likainen. Puhdista tunnistin ja yritä uudelleen."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Liikutit sormea liian hitaasti. Yritä uudelleen."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ei tunnistettu"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ei tunnistettu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Sormenjälki tunnistettu"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Sormenjälkilaitteisto ei ole käytettävissä."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sormenjälkeä ei voida tallentaa. Poista aiemmin lisätty sormenjälki."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 2409202..0c0cdea 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -253,7 +253,7 @@
<string name="notification_channel_network_alerts" msgid="2895141221414156525">"Alertes réseau"</string>
<string name="notification_channel_network_available" msgid="4531717914138179517">"Réseau accessible"</string>
<string name="notification_channel_vpn" msgid="8330103431055860618">"État du RPV"</string>
- <string name="notification_channel_device_admin" msgid="1568154104368069249">"Administration d\'appareils"</string>
+ <string name="notification_channel_device_admin" msgid="1568154104368069249">"Gestion de l\'appareil"</string>
<string name="notification_channel_alerts" msgid="4496839309318519037">"Alertes"</string>
<string name="notification_channel_retail_mode" msgid="6088920674914038779">"Démo en magasin"</string>
<string name="notification_channel_usb" msgid="9006850475328924681">"Connexion USB"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Autorise l\'application à modifier votre collection de photos."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"lire les positions issues de votre collection multimédia"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Autorise l\'application à lire les positions indiquées dans votre collection multimédia."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Matériel biométrique indisponible"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte digitale partielle détectée. Veuillez essayer de nouveau."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de traiter les empreintes digitales. Veuillez essayer de nouveau."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes digitales est sale. Veuillez le nettoyer et essayer de nouveau."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Doigt non reconnu"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Données biométriques non reconnues"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Empreinte digitale authentifiée"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte numérique indisponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empreinte digitale ne peut pas être enregistrée. Veuillez supprimer une empreinte existante."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 9c8a7e7..6932be3 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Autorise l\'application à modifier votre bibliothèque photo."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"consulter des positions issues de votre bibliothèque multimédia"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Autorise l\'application à consulter des positions issues de votre bibliothèque multimédia."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Matériel biométrique indisponible"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte numérique partiellement détectée. Veuillez réessayer."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le lecteur d\'empreintes numériques est sale. Veuillez le nettoyer, puis réessayer."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Non reconnu"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Non reconnu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Empreinte digitale authentifiée"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Matériel d\'empreinte numérique indisponible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossible d\'enregistrer l\'empreinte numérique. Veuillez supprimer une empreinte."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 27efff8..ba66adf 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que a aplicación modifique a túa colección de fotos."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ler localizacións da túa colección multimedia"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que a aplicación lea as localizacións da túa colección multimedia."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"O hardware biométrico non está dispoñible"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Detectouse unha impresión dixital parcial. Téntao de novo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Non se puido procesar a impresión dixital. Téntao de novo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impresión dixital está sucio. Límpao e téntao de novo."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"O dedo moveuse demasiado lento. Téntao de novo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Non se recoñece"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Non se recoñeceu"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Autenticouse a impresión dixital"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware de impresión dixital non dispoñible."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Non se pode almacenar a impresión dixital. Elimina unha impresión dixital existente."</string>
@@ -1891,8 +1892,8 @@
</plurals>
<string name="autofill_save_title" msgid="3345527308992082601">"Queres gardar o contido en: <b><xliff:g id="LABEL">%1$s</xliff:g></b>?"</string>
<string name="autofill_save_title_with_type" msgid="8637809388029313305">"Queres gardar <xliff:g id="TYPE">%1$s</xliff:g> en: <b><xliff:g id="LABEL">%2$s</xliff:g></b>?"</string>
- <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Queres gardar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> en: <b><xliff:g id="LABEL">%3$s</xliff:g></b>?"</string>
- <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Queres gardar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> en: <b><xliff:g id="LABEL">%4$s</xliff:g></b>?"</string>
+ <string name="autofill_save_title_with_2types" msgid="5214035651838265325">"Queres gardar estes datos (<xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g>) en <b><xliff:g id="LABEL">%3$s</xliff:g></b>?"</string>
+ <string name="autofill_save_title_with_3types" msgid="6943161834231458441">"Queres gardar estes datos (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>) en <b><xliff:g id="LABEL">%4$s</xliff:g></b>?"</string>
<string name="autofill_update_title" msgid="4879673117448810818">"Queres actualizar <b><xliff:g id="LABEL">%1$s</xliff:g></b> con estes datos?"</string>
<string name="autofill_update_title_with_type" msgid="339733442087186755">"Queres actualizar <b><xliff:g id="LABEL">%2$s</xliff:g></b> con estes datos (<xliff:g id="TYPE">%1$s</xliff:g>)?"</string>
<string name="autofill_update_title_with_2types" msgid="6321714204167424745">"Queres actualizar <b><xliff:g id="LABEL">%3$s</xliff:g></b> con estes datos (<xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g>)?"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5e6c34d..2b96f3f 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"એપને તમારો ફોટો સંગ્રહ સંશોધિત કરવાની મંજૂરી આપે છે."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"આપના મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવા"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"એપને તમારા મીડિયા સંગ્રહમાંથી સ્થાનો વાંચવાની મંજૂરી આપે છે."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"બાયોમેટ્રિક હાર્ડવેર ઉપલબ્ધ નથી"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"આંશિક ફિંગરપ્રિન્ટ મળી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ફિંગરપ્રિન્ટ પ્રક્રિયા કરી શકાઈ નથી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ફિંગરપ્રિન્ટ સેન્સર ગંદું છે. કૃપા કરીને સાફ કરો અને ફરી પ્રયાસ કરો."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"આંગળી બહુ જ ધીમેથી ખસેડી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"ઓળખાયેલ નથી"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ઓળખાયેલ નથી"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ફિંગરપ્રિન્ટ પ્રમાણિત કરી"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ફિંગરપ્રિન્ટ હાર્ડવેર ઉપલબ્ધ નથી."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ફિંગરપ્રિન્ટ સંગ્રહિત કરી શકાતી નથી. કૃપા કરીને અસ્તિત્વમાંની ફિંગરપ્રિન્ટ દૂર કરો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index a85d6fa..d4b800a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -300,18 +300,12 @@
<string name="permgrouplab_sensors" msgid="416037179223226722">"शरीर संवेदक"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"अपने महत्वपूर्ण संकेतों के बारे में सेंसर डेटा को ऐक्सेस करें"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपने स्वास्थ्य से जुड़ी ज़रूरी जानकारी इस्तेमाल करने की अनुमति देना चाहते हैं?"</string>
- <!-- no translation found for permgrouplab_aural (965607064083134896) -->
- <skip />
- <!-- no translation found for permgroupdesc_aural (4870189506255958055) -->
- <skip />
- <!-- no translation found for permgrouprequest_aural (6787926123071735620) -->
- <skip />
- <!-- no translation found for permgrouplab_visual (8030190588123857921) -->
- <skip />
- <!-- no translation found for permgroupdesc_visual (3415827902566663546) -->
- <skip />
- <!-- no translation found for permgrouprequest_visual (6907523945030290376) -->
- <skip />
+ <string name="permgrouplab_aural" msgid="965607064083134896">"संगीत"</string>
+ <string name="permgroupdesc_aural" msgid="4870189506255958055">"अपना संगीत संग्रह एक्सेस करने दें"</string>
+ <string name="permgrouprequest_aural" msgid="6787926123071735620">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपना संगीत एक्सेस करने दें?"</string>
+ <string name="permgrouplab_visual" msgid="8030190588123857921">"फ़ोटो और वीडियो"</string>
+ <string name="permgroupdesc_visual" msgid="3415827902566663546">"अपने फ़ोटो और वीडियो एक्सेस करने दें"</string>
+ <string name="permgrouprequest_visual" msgid="6907523945030290376">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> को अपने फ़ोटो और वीडियो एक्सेस करने दें?"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="3901717936930170320">"विंडो की सामग्री वापस पाएं"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="3772225008605310672">"उस विंडो की सामग्री की जाँच करें, जिसका आप इस्तेमाल कर रहे हैं."</string>
<string name="capability_title_canRequestTouchExploration" msgid="3108723364676667320">"छूकर, किसी चीज़ से जुड़ी जानकारी सुनने की सुविधा चालू करें"</string>
@@ -506,33 +500,21 @@
<string name="permdesc_manageFingerprint" msgid="178208705828055464">"उंगली की छाप वाले टेम्पलेट का उपयोग करने के लिए जोड़ने और हटाने हेतु ऐप को विधियां प्रारंभ करने देती है."</string>
<string name="permlab_useFingerprint" msgid="3150478619915124905">"उंगली की छाप के लिए हार्डवेयर का उपयोग करें"</string>
<string name="permdesc_useFingerprint" msgid="9165097460730684114">"ऐप के प्रमाणीकरण के लिए उंगली की छाप हार्डवेयर का उपयोग करने देती है"</string>
- <!-- no translation found for permlab_audioRead (6617225220728465565) -->
- <skip />
- <!-- no translation found for permdesc_audioRead (5034032570243484805) -->
- <skip />
- <!-- no translation found for permlab_audioWrite (2661772059799779292) -->
- <skip />
- <!-- no translation found for permdesc_audioWrite (8888544708166230494) -->
- <skip />
- <!-- no translation found for permlab_videoRead (9182618678674737229) -->
- <skip />
- <!-- no translation found for permdesc_videoRead (7045676429859396194) -->
- <skip />
- <!-- no translation found for permlab_videoWrite (128769316366746446) -->
- <skip />
- <!-- no translation found for permdesc_videoWrite (5448565757490640841) -->
- <skip />
- <!-- no translation found for permlab_imagesRead (3015078545742665304) -->
- <skip />
- <!-- no translation found for permdesc_imagesRead (3144263806038695580) -->
- <skip />
- <!-- no translation found for permlab_imagesWrite (3391306186247235510) -->
- <skip />
- <!-- no translation found for permdesc_imagesWrite (7073662756617474375) -->
- <skip />
- <!-- no translation found for permlab_mediaLocation (8675148183726247864) -->
- <skip />
- <!-- no translation found for permdesc_mediaLocation (2237023389178865130) -->
+ <string name="permlab_audioRead" msgid="6617225220728465565">"अपना संगीत संग्रह एक्सेस करने की अनुमति दें"</string>
+ <string name="permdesc_audioRead" msgid="5034032570243484805">"इससे ऐप्लिकेशन को आपके संगीत संग्रह को एक्सेस करने की मंज़ूरी दी जाती है."</string>
+ <string name="permlab_audioWrite" msgid="2661772059799779292">"अपने संगीत संग्रह में बदलाव करने की अनुमति दें"</string>
+ <string name="permdesc_audioWrite" msgid="8888544708166230494">"इससे ऐप्लिकेशन को आपके संगीत संग्रह में बदलाव करने की मंज़ूरी दी जाती है"</string>
+ <string name="permlab_videoRead" msgid="9182618678674737229">"अपना वीडियो संग्रह एक्सेस करने की अनुमति दें"</string>
+ <string name="permdesc_videoRead" msgid="7045676429859396194">"इससे ऐप्लिकेशन को आपके वीडियो संग्रह को एक्सेस करने की मंज़ूरी दी जाती है."</string>
+ <string name="permlab_videoWrite" msgid="128769316366746446">"अपने वीडियो संग्रह में बदलाव करने की अनुमति दें"</string>
+ <string name="permdesc_videoWrite" msgid="5448565757490640841">"इससे ऐप्लिकेशन को आपके वीडियो संग्रह में बदलाव करने की मंज़ूरी दी जाती है."</string>
+ <string name="permlab_imagesRead" msgid="3015078545742665304">"अपना फ़ोटो संग्रह एक्सेस करने की अनुमति दें"</string>
+ <string name="permdesc_imagesRead" msgid="3144263806038695580">"इससे ऐप्लिकेशन को आपके फ़ोटो संग्रह को एक्सेस करने की मंज़ूरी दी जाती है."</string>
+ <string name="permlab_imagesWrite" msgid="3391306186247235510">"अपने फ़ोटो संग्रह में बदलाव करने की अनुमति दें"</string>
+ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"इससे ऐप्लिकेशन को आपके फ़ोटो संग्रह में बदलाव करने की मंज़ूरी दी जाती है."</string>
+ <string name="permlab_mediaLocation" msgid="8675148183726247864">"अपने मीडिया संग्रह से जगह की जानकारी एक्सेस करने की अनुमति दें"</string>
+ <string name="permdesc_mediaLocation" msgid="2237023389178865130">"इससे ऐप्लिकेशन को आपके मीडिया संग्रह से जगह की जानकारी एक्सेस करने की अनुमति दी जाती है."</string>
+ <!-- no translation found for biometric_error_hw_unavailable (645781226537551036) -->
<skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फ़िंगरप्रिंट की पहचान की गई. कृपया पुनः प्रयास करें."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फ़िंगरप्रिंट संसाधित नहीं हो सका. कृपया पुन: प्रयास करें."</string>
@@ -541,7 +523,8 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"उंगली बहुत धीरे चलाई गई. कृपया फिर से कोशिश करें."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"पहचाना नहीं गया"</string>
+ <!-- no translation found for biometric_not_recognized (5770511773560736082) -->
+ <skip />
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहित नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string>
@@ -1912,18 +1895,13 @@
<string name="autofill_save_title_with_type" msgid="8637809388029313305">"<xliff:g id="TYPE">%1$s</xliff:g> को <b><xliff:g id="LABEL">%2$s</xliff:g></b> में सेव करें?"</string>
<string name="autofill_save_title_with_2types" msgid="5214035651838265325">"<xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> को <b><xliff:g id="LABEL">%3$s</xliff:g></b> में सेव करें?"</string>
<string name="autofill_save_title_with_3types" msgid="6943161834231458441">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को <b><xliff:g id="LABEL">%4$s</xliff:g></b> में सेव करें?"</string>
- <!-- no translation found for autofill_update_title (4879673117448810818) -->
- <skip />
- <!-- no translation found for autofill_update_title_with_type (339733442087186755) -->
- <skip />
- <!-- no translation found for autofill_update_title_with_2types (6321714204167424745) -->
- <skip />
- <!-- no translation found for autofill_update_title_with_3types (5866735124066629287) -->
- <skip />
+ <string name="autofill_update_title" msgid="4879673117448810818">"<b><xliff:g id="LABEL">%1$s</xliff:g></b>? में अपडेट करें?"</string>
+ <string name="autofill_update_title_with_type" msgid="339733442087186755">"<xliff:g id="TYPE">%1$s</xliff:g> को to <b><xliff:g id="LABEL">%2$s</xliff:g></b> में अपडेट करें?"</string>
+ <string name="autofill_update_title_with_2types" msgid="6321714204167424745">"<xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> को <b><xliff:g id="LABEL">%3$s</xliff:g></b> में अपडेट करें?"</string>
+ <string name="autofill_update_title_with_3types" msgid="5866735124066629287">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> और <xliff:g id="TYPE_2">%3$s</xliff:g> को <b><xliff:g id="LABEL">%4$s</xliff:g></b> में अपडेट करें."</string>
<string name="autofill_save_yes" msgid="6398026094049005921">"सेव करें"</string>
<string name="autofill_save_no" msgid="2625132258725581787">"नहीं, धन्यवाद"</string>
- <!-- no translation found for autofill_update_yes (310358413273276958) -->
- <skip />
+ <string name="autofill_update_yes" msgid="310358413273276958">"अपडेट करें"</string>
<string name="autofill_save_type_password" msgid="5288448918465971568">"पासवर्ड"</string>
<string name="autofill_save_type_address" msgid="4936707762193009542">"पता"</string>
<string name="autofill_save_type_credit_card" msgid="7127694776265563071">"क्रेडिट कार्ड"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index f24a3c4..e7b6b5f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -517,6 +517,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Omogućuje aplikaciji izmjenu vaše zbirke fotografija."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"čitanje lokacija iz vaše medijske zbirke"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Omogućuje aplikaciji čitanje lokacija iz vaše medijske zbirke."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrijski hardver nije dostupan"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je djelomični otisak prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor otiska prsta nije čist. Očistite ga i pokušajte ponovo."</string>
@@ -524,7 +525,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Presporo pomicanje prsta. Pokušajte ponovo."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nije prepoznat"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nije prepoznato"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Autentificirano otiskom prsta"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardver za otisak prsta nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta nije pohranjen. Uklonite postojeći otisak prsta."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index e7c1bf2..3d9a35b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Engedélyezi az alkalmazásnak a fényképgyűjtemény módosítását."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"helyek olvasása a médiagyűjteményből"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Engedélyezi az alkalmazásnak a helyek médiagyűjteményből való olvasását."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrikus hardver nem áll rendelkezésre"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"A rendszer az ujjlenyomatnak csak egy részletét érzékelte. Próbálkozzon újra."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Az ujjlenyomat-olvasó koszos. Tisztítsa meg, majd próbálkozzon újra."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Túl lassan húzta az ujját. Próbálkozzon újra."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nem sikerült felismerni"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nem ismerhető fel"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Ujjlenyomat hitelesítve"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Az ujjlenyomathoz szükséges hardver nem érhető el."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Az ujjlenyomat nem tárolható. Távolítson el egy meglévő ujjlenyomatot."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index a46d5e6..8b3f287 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Թույլ է տալիս հավելվածին փոփոխել ձեր լուսանկարների հավաքածուն:"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ճանաչել տեղադրության մասին տվյալները մեդիա բովանդակության հավաքածուից"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Թույլ է տալիս հավելվածին ճանաչել տեղադրության մասին տվյալները ձեր մեդիա բովանդակության հավաքածուից:"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Կենսաչափական սարք չի գտնվել"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքը հայտնաբերվել է մասամբ: Փորձեք նորից:"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Մատնահետքերի սենսորն աղտոտված է: Մաքրեք այն և փորձեք նորից:"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Շատ դանդաղ անցկացրեցիք մատը: Փորձեք նորից:"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Չճանաչվեց"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Չհաջողվեց ճանաչել"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Մատնահետքը նույնականացվեց"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Մատնահետքի սարքն անհասանելի է:"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Հնարավոր չէ պահել մատնահետքը: Հեռացրեք առկա մատնահետքը:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index ed5444d..6d1da48 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Mengizinkan aplikasi untuk memodifikasi koleksi foto Anda."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"membaca lokasi dari koleksi media Anda"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Mengizinkan aplikasi untuk membaca lokasi dari koleksi media Anda."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrik tidak tersedia"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sebagian sidik jari terdeteksi. Coba lagi."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses sidik jari. Coba lagi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensor sidik jari kotor. Bersihkan dan coba lagi."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Jari digerakkan terlalu lambat. Coba lagi."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Tidak dikenali"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tidak dikenali"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Sidik jari diautentikasi"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware sidik jari tidak tersedia."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sidik jari tidak dapat disimpan. Hapus sidik jari yang ada."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 330b010..4d7996f 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Leyfir forritinu að breyta myndasafninu þínu."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"lesa staðsetningar úr efnissafninu þínu"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Leyfir forritinu að lesa staðsetningar úr efnissafninu þínu."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Lífkennavélbúnaður ekki tiltækur"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hluti fingrafars greindist. Reyndu aftur."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ekki var hægt að vinna úr fingrafarinu. Reyndu aftur."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingrafaraskynjarinn er óhreinn. Hreinsaðu hann og reyndu aftur."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Fingurinn hreyfðist of hægt. Reyndu aftur."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Þekktist ekki"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Þekktist ekki"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingrafar staðfest"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Fingrafarsvélbúnaður ekki til staðar."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Ekki er hægt að vista fingrafarið. Fjarlægðu eitthvert af fingraförunum sem fyrir eru."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index a7c1ae2..44818a3 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Consente all\'app di modificare la tua raccolta di foto."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"Lettura delle posizioni dalla tua raccolta multimediale"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Consente all\'app di leggere le posizioni dalla tua raccolta multimediale."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometrico non disponibile"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Rilevata impronta digitale parziale. Riprova."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossibile elaborare l\'impronta digitale. Riprova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Il sensore di impronte digitali è sporco. Puliscilo e riprova."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Movimento del dito troppo lento. Riprova."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Non riconosciuto"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Non riconosciuto"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Impronta digitale autenticata"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware per l\'impronta digitale non disponibile."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossibile memorizzare l\'impronta digitale. Rimuovi un\'impronta esistente."</string>
@@ -832,7 +833,7 @@
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Hai dimenticato la sequenza?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="2588521501166032747">"Sblocco account"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"Troppi tentativi di inserimento della sequenza"</string>
- <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"Per sbloccare, accedi tramite il tuo account Google."</string>
+ <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"Per sbloccare, accedi tramite il tuo Account Google."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nome utente (email)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Password"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Accedi"</string>
@@ -1585,7 +1586,7 @@
<string name="kg_invalid_puk" msgid="3638289409676051243">"Inserisci di nuovo il codice PUK corretto. Ripetuti tentativi comportano la disattivazione definitiva della scheda SIM."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"I codici PIN non corrispondono"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Troppi tentativi di inserimento della sequenza"</string>
- <string name="kg_login_instructions" msgid="1100551261265506448">"Per sbloccare, accedi con il tuo account Google."</string>
+ <string name="kg_login_instructions" msgid="1100551261265506448">"Per sbloccare, accedi con il tuo Account Google."</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"Nome utente (email)"</string>
<string name="kg_login_password_hint" msgid="9057289103827298549">"Password"</string>
<string name="kg_login_submit_button" msgid="5355904582674054702">"Accedi"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 9a3fe87..fd5c2a1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"מאפשרת לאפליקציה לשנות את אוסף התמונות שלך."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"לקרוא מיקומים מאוסף המדיה שלך"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"מאפשרת לאפליקציה לקרוא מיקומים מאוסף המדיה שלך."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"חומרה ביומטרית לא זמינה"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"זוהתה טביעת אצבע חלקית. נסה שוב."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"לא ניתן היה לעבד את טביעת האצבע. נסה שוב."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"החיישן של טביעות האצבעות מלוכלך. נקה אותו ונסה שוב."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"הזזת את האצבע לאט מדי. נסה שוב."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"לא זוהתה"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"לא זוהתה"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"טביעת האצבע אומתה"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"החומרה בשביל טביעת אצבע אינה זמינה."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"לא ניתן לאחסן טביעת אצבע. הסר טביעת אצבע קיימת."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index ec2ada4..4cada60 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"写真コレクションの変更をアプリに許可します。"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"メディア コレクションの位置情報の読み取り"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"メディア コレクションの位置情報の読み取りをアプリに許可します。"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"生体認証ハードウェアが利用できません"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"指紋を一部しか検出できませんでした。もう一度お試しください。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"指紋を処理できませんでした。もう一度お試しください。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋認証センサーに汚れがあります。汚れを落としてもう一度お試しください。"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"指の動きが遅すぎました。もう一度お試しください。"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"認識されませんでした"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"認識されませんでした"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"指紋認証を完了しました"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"指紋ハードウェアは使用できません。"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"指紋を保存できません。既存の指紋を削除してください。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 0a383df..51a4c28 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"აპი შეძლებს თქვენი ფოტოკოლექციის შეცვლას."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"მდებარეობების გაცნობა თქვენი მედიაკოლექციიდან"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"აპი შეძლებს მდებარეობების გაცნობას თქვენი მედიაკოლექციიდან."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ბიომეტრიული აპარატურა მიუწვდომელია"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"აღმოჩენილია თითის ნაწილობრივი ანაბეჭდი. გთხოვთ, სცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"თითის ანაბეჭდი ვერ მუშავდება. გთხოვთ, სცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"თითის ანაბეჭდის სენსორი დაბინძურებულია. გთხოვთ, გაასუფთაოთ და სცადოთ ხელახლა."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"თითის აღება მეტისმეტად ნელა მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"არ არის ამოცნობილი"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"არ არის ამოცნობილი"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"თითის ანაბეჭდი ავტორიზებულია"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"თითის ანაბეჭდის აპარატურა არ არის ხელმისაწვდომი."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"თითის ანაბეჭდის შენახვა ვერ ხერხდება. გთხოვთ, ამოშალოთ არსებული თითის ანაბეჭდი."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 2509958..18be1e6 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Қолданбаға суреттер жинағын өзгертуге мүмкіндік береді."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"медиамазмұн жинағынан геодеректерді оқу"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Қолданбаға медиамазмұн жинағынан геодеректерді оқуға мүмкіндік береді."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрикалық жабдық жоқ"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Саусақ ізі ішінара анықталды. Әрекетті қайталаңыз."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Саусақ ізін өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Саусақ ізі сенсоры лас. Тазалап, әрекетті қайталаңыз."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Саусағыңызды тым баяу қозғалттыңыз. Әрекетті қайталап көріңіз."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Анықталмаған"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Танылмады"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Саусақ ізі аутентификацияланды"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Саусақ ізі жабдығы қолжетімді емес."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Саусақ ізін сақтау мүмкін емес. Бар саусақ ізін жойыңыз."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index e24c9a0..091fa71 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"អនុញ្ញាតឱ្យកម្មវិធីកែប្រែបណ្ដុំរូបថតរបស់អ្នក។"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"អានទីតាំងពីបណ្ដុំមេឌៀរបស់អ្នក"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"អនុញ្ញាតឱ្យកម្មវិធីអានទីតាំងពីបណ្ដុំមេឌៀរបស់អ្នក។"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"មិនអាចប្រើឧបករណ៍ស្កេនស្នាមម្រាមដៃបានទេ"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"បានផ្តិតយកស្នាមម្រាមដៃមិនពេញលក្ខណៈ។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ឧបករណ៍ផ្តិតម្រាមដៃប្រលាក់ហើយ។ សូមសម្អាត ហើយព្យាយាមម្តងទៀត។"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ចលនាម្រាមដៃយឺតពេកហើយ។ សូមព្យាយាមម្តងទៀត។"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"មិនអាចសម្គាល់បានទេ"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"មិនអាចសម្គាល់បានទេ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"បានផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃ"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ផ្នែករឹងស្នាមម្រាមដៃមិនមានទេ។"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"មិនអាចផ្ទុកស្នាមម្រាមដៃទេ។ សូមយកស្នាមម្រាមដៃដែលមានស្រាប់ចេញ។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 158a316..dca94ff 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"ನಿಮ್ಮ ಫೋಟೋ ಸಂಗ್ರಹಣೆಯನ್ನು ಮಾರ್ಪಡಿಸಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಿ"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ನಿಮ್ಮ ಮೀಡಿಯಾ ಸಂಗ್ರಹಣೆಯಿಂದ ಸ್ಥಳಗಳನ್ನು ಓದಲು ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ಭಾಗಶಃ ಬೆರಳಚ್ಚು ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ಬೆರಳಚ್ಚು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ಬೆರಳಚ್ಚು ಸೆನ್ಸಾರ್ ಕೊಳೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ಬೆರಳನ್ನು ತುಂಬಾ ನಿಧಾನವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ಗುರುತಿಸಲಾಗಿಲ್ಲ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಮಾಣೀಕರಣ ಮಾಡಲಾಗಿದೆ"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ಬೆರಳಚ್ಚು ಹಾರ್ಡ್ವೇರ್ ಲಭ್ಯವಿಲ್ಲ."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ಬೆರಳಚ್ಚು ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಬೆರಳಚ್ಚು ತೆಗೆದುಹಾಕಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 776f773..d37a475 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -284,7 +284,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 SMS 메시지를 전송하고 보도록 허용하시겠습니까?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"저장용량"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"기기 사진, 미디어, 파일 액세스"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 기기의 사진, 미디어, 파일에 액세스하도록 허용하시겠습니까.?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 기기의 사진, 미디어, 파일에 액세스하도록 허용하시겠습니까?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"마이크"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"오디오 녹음"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>에서 오디오를 녹음하도록 허용하시겠습니까?"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"앱에서 사진 컬렉션을 수정하도록 허용합니다."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"미디어 컬렉션에서 위치 읽기"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"앱에서 미디어 컬렉션의 위치를 읽도록 허용합니다."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"생체 인식 하드웨어를 사용할 수 없음"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"지문이 일부만 인식되었습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"지문을 인식할 수 없습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"지문 센서를 깨끗이 닦고 다시 시도하세요."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"손가락을 너무 느리게 움직였습니다. 다시 시도해 주세요."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"인식할 수 없습니다."</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"인식할 수 없음"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"지문이 인증됨"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"지문 인식 하드웨어를 사용할 수 없습니다."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"지문을 저장할 수 없습니다. 기존 지문을 삭제하세요."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 1c1e9a9..cf5f531 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Колдонмого сүрөт жыйнагыңызды өзгөртүүгө мүмкүнчүлүк берет."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"медиа жыйнагыңыз сакталган жерлерди окуу"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Колдонмого медиа жыйнагыңыз сакталган жерлерди окууга мүмкүнчүлүк берет."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрикалык аппарат жеткиликсиз"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Манжа изи жарым-жартылай аныкталды. Кайра аракет кылыңыз."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Манжа изи иштелбей койду. Кайра аракет кылыңыз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Манжа изинин сенсору кирдеп калган. Тазалап, кайра аракет кылыңыз."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Манжа өтө жай жылды. Кайра аракет кылыңыз."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Таанылган жок"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Таанылган жок"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Манжа изинин аныктыгы текшерилди"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Манжа изинин аппараттык камсыздоосу жеткиликтүү эмес."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Манжа изин сактоо мүмкүн эмес. Учурдагы манжа изин алып салыңыз."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index b17b5ab..7d8a4e2 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"ອະນຸຍາດໃຫ້ແອັບແກ້ໄຂຄໍເລັກຊັນຮູບຂອງທ່ານ."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ອ່ານສະຖານທີ່ຈາກຄໍເລັກຊັນມີເດຍຂອງທ່ານ"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ອະນຸຍາດໃຫ້ແອັບອ່ານສະຖານທີ່ຈາກຄໍເລັກຊັນມີເດຍຂອງທ່ານ."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ຮາດແວຊີວະມິຕິບໍ່ສາມາດໃຊ້ໄດ້"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ກວດພົບລາຍນີ້ວມືບາງສ່ວນແລ້ວ. ກະລຸນາລອງໃໝ່ອີກ."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ບໍ່ສາມາດດຳເນີນການລາຍນີ້ວມືໄດ້. ກະລຸນາລອງໃໝ່ອີກ."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ເຊັນເຊີລາຍນີ້ວມືເປື້ອນ. ກະລຸນາທຳຄວາມສະອາດ ແລະລອງໃໝ່ອີກ."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ຍ້າຍນີ້ວມືໄປຊ້າເກີນໄປ. ກະລຸນາລອງໃໝ່ອີກ."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"ບໍ່ຈົດຈຳໄດ້"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ບໍ່ຮັບຮູ້"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ພິສູດຢືນຢັນລາຍນິ້ວມືແລ້ວ"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ບໍ່ມີຮາດແວລາຍນີ້ວມືໃຫ້ຢູ່."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ບໍ່ສາມາດເກັບຮັກສາລາຍນີ້ວມືໄວ້ໄດ້. ກະລຸນາເອົາລາຍນີ້ວມືທີ່ມີຢູ່ອອກໄປ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 9409c7d..a0015c3 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Programai leidžiama keisti nuotraukų kolekciją."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"skaityti vietoves iš medijos kolekcijos"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Programai leidžiama skaityti vietoves iš medijos kolekcijos."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrinė aparatinė įranga nepasiekiama"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Aptiktas dalinis kontrolinis kodas. Bandykite dar kartą."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nepavyko apdoroti kontrolinio kodo. Bandykite dar kartą."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Kontrolinio kodo jutiklis purvinas. Nuvalykite ir bandykite dar kartą."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Per lėtai judinate pirštą. Bandykite dar kartą."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Neatpažintas"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Neatpažinta"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Kontrolinis kodas autentifikuotas"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Kontrolinio kodo aparatinė įranga nepasiekiama."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Negalima išsaugoti kontrolinio kodo. Pašalinkite esamą kontrolinį kodą."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 272deb3..a1aebe1 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -517,6 +517,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Ļauj lietotnei pārveidot jūsu fotoattēlu kolekciju."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"Lasīt atrašanās vietas no jūsu multivides kolekcijas"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ļauj lietotnei lasīt atrašanās vietas no jūsu multivides kolekcijas."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisko datu aparatūra nav pieejama"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Noteikts daļējs pirksta nospiedums. Lūdzu, mēģiniet vēlreiz."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nevarēja apstrādāt pirksta nospiedumu. Lūdzu, mēģiniet vēlreiz."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Pirkstu nospiedumu sensors ir netīrs. Lūdzu, notīriet to un mēģiniet vēlreiz."</string>
@@ -524,7 +525,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Pārāk lēna pirksta kustība. Lūdzu, mēģiniet vēlreiz."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nav atpazīts"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Dati nav atpazīti"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Pirksta nospiedums tika autentificēts."</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Nospieduma aparatūra nav pieejama."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Pirkstu nospiedumu nevar saglabāt. Lūdzu, noņemiet esošu pirksta nospiedumu."</string>
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index 3308128..13f6bce 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -40,7 +40,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=3</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
index bb0d4d5..d45b91a 100644
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ b/core/res/res/values-mcc302-mnc221/config.xml
@@ -38,7 +38,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=3</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml
index 1241a9d..b520d5d 100644
--- a/core/res/res/values-mcc302-mnc370/config.xml
+++ b/core/res/res/values-mcc302-mnc370/config.xml
@@ -41,7 +41,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=2</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc610/config.xml b/core/res/res/values-mcc302-mnc610/config.xml
index 232f149..650aa62 100644
--- a/core/res/res/values-mcc302-mnc610/config.xml
+++ b/core/res/res/values-mcc302-mnc610/config.xml
@@ -28,7 +28,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=2</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc640/config.xml b/core/res/res/values-mcc302-mnc640/config.xml
index 1d2e625..4bb68dc 100644
--- a/core/res/res/values-mcc302-mnc640/config.xml
+++ b/core/res/res/values-mcc302-mnc640/config.xml
@@ -24,7 +24,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=2</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml
index ef1ecd2..11bfa05 100644
--- a/core/res/res/values-mcc302-mnc720/config.xml
+++ b/core/res/res/values-mcc302-mnc720/config.xml
@@ -43,7 +43,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=2</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
diff --git a/core/res/res/values-mcc310-mnc030-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc030-pt-rBR/strings.xml
index 663261c..60a143f 100644
--- a/core/res/res/values-mcc310-mnc030-pt-rBR/strings.xml
+++ b/core/res/res/values-mcc310-mnc030-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6575159319460304530">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="1930079814544869756">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6575159319460304530">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="1930079814544869756">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="2995576894416087107">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc030-pt/strings.xml b/core/res/res/values-mcc310-mnc030-pt/strings.xml
index 663261c..60a143f 100644
--- a/core/res/res/values-mcc310-mnc030-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc030-pt/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6575159319460304530">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="1930079814544869756">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6575159319460304530">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="1930079814544869756">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="2995576894416087107">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc170-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc170-pt-rBR/strings.xml
index fcffa16..c774e95 100644
--- a/core/res/res/values-mcc310-mnc170-pt-rBR/strings.xml
+++ b/core/res/res/values-mcc310-mnc170-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="210168420192421012">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="1130721094178658338">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="210168420192421012">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="1130721094178658338">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="3173546391131606065">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc170-pt/strings.xml b/core/res/res/values-mcc310-mnc170-pt/strings.xml
index fcffa16..c774e95 100644
--- a/core/res/res/values-mcc310-mnc170-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc170-pt/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="210168420192421012">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="1130721094178658338">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="210168420192421012">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="1130721094178658338">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="3173546391131606065">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc280-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc280-pt-rBR/strings.xml
index f7fb684..3a269b6 100644
--- a/core/res/res/values-mcc310-mnc280-pt-rBR/strings.xml
+++ b/core/res/res/values-mcc310-mnc280-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6638755728961013003">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="5562215652599183258">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6638755728961013003">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="5562215652599183258">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="822496463303720579">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc280-pt/strings.xml b/core/res/res/values-mcc310-mnc280-pt/strings.xml
index f7fb684..3a269b6 100644
--- a/core/res/res/values-mcc310-mnc280-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc280-pt/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6638755728961013003">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="5562215652599183258">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6638755728961013003">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="5562215652599183258">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="822496463303720579">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc380-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc380-pt-rBR/strings.xml
index 06bc55a..6f8fb21 100644
--- a/core/res/res/values-mcc310-mnc380-pt-rBR/strings.xml
+++ b/core/res/res/values-mcc310-mnc380-pt-rBR/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6933439408719203102">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="6367773216941648568">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6933439408719203102">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="6367773216941648568">"Chip não permitido MM#3"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc380-pt/strings.xml b/core/res/res/values-mcc310-mnc380-pt/strings.xml
index 06bc55a..6f8fb21 100644
--- a/core/res/res/values-mcc310-mnc380-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc380-pt/strings.xml
@@ -20,6 +20,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="6933439408719203102">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="6367773216941648568">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="6933439408719203102">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="6367773216941648568">"Chip não permitido MM#3"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc410-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc410-pt-rBR/strings.xml
index f6bfc67..f80ff85 100644
--- a/core/res/res/values-mcc310-mnc410-pt-rBR/strings.xml
+++ b/core/res/res/values-mcc310-mnc410-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="376893116792604964">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="1593063035884873292">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="376893116792604964">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="1593063035884873292">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="4477688981805467729">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc410-pt/strings.xml b/core/res/res/values-mcc310-mnc410-pt/strings.xml
index f6bfc67..f80ff85 100644
--- a/core/res/res/values-mcc310-mnc410-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc410-pt/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="376893116792604964">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="1593063035884873292">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="376893116792604964">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="1593063035884873292">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="4477688981805467729">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc560-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc560-pt-rBR/strings.xml
index fba400c..1d16be7 100644
--- a/core/res/res/values-mcc310-mnc560-pt-rBR/strings.xml
+++ b/core/res/res/values-mcc310-mnc560-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="2976453378311251765">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="2519618694918727742">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="2976453378311251765">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="2519618694918727742">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="7030488670186895244">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc560-pt/strings.xml b/core/res/res/values-mcc310-mnc560-pt/strings.xml
index fba400c..1d16be7 100644
--- a/core/res/res/values-mcc310-mnc560-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc560-pt/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="2976453378311251765">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="2519618694918727742">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="2976453378311251765">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="2519618694918727742">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="7030488670186895244">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc950-pt-rBR/strings.xml b/core/res/res/values-mcc310-mnc950-pt-rBR/strings.xml
index 83c84ce..e74582c 100644
--- a/core/res/res/values-mcc310-mnc950-pt-rBR/strings.xml
+++ b/core/res/res/values-mcc310-mnc950-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="94675382531896663">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="2418195136279399212">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="94675382531896663">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="2418195136279399212">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="8920048244573695129">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc950-pt/strings.xml b/core/res/res/values-mcc310-mnc950-pt/strings.xml
index 83c84ce..e74582c 100644
--- a/core/res/res/values-mcc310-mnc950-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc950-pt/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="94675382531896663">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="2418195136279399212">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="94675382531896663">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="2418195136279399212">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="8920048244573695129">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc180-pt-rBR/strings.xml b/core/res/res/values-mcc311-mnc180-pt-rBR/strings.xml
index 637a91c..beb1715 100644
--- a/core/res/res/values-mcc311-mnc180-pt-rBR/strings.xml
+++ b/core/res/res/values-mcc311-mnc180-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="531930017979728896">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="97745044956236881">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="531930017979728896">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="97745044956236881">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="5766888847785331904">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc180-pt/strings.xml b/core/res/res/values-mcc311-mnc180-pt/strings.xml
index 637a91c..beb1715 100644
--- a/core/res/res/values-mcc311-mnc180-pt/strings.xml
+++ b/core/res/res/values-mcc311-mnc180-pt/strings.xml
@@ -20,7 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="mmcc_imsi_unknown_in_hlr" msgid="531930017979728896">"SIM não aprovisionado MM#2"</string>
- <string name="mmcc_illegal_ms" msgid="97745044956236881">"SIM não permitido MM#3"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="531930017979728896">"Chip não aprovisionado MM#2"</string>
+ <string name="mmcc_illegal_ms" msgid="97745044956236881">"Chip não permitido MM#3"</string>
<string name="mmcc_illegal_me" msgid="5766888847785331904">"Smartphone não permitido MM#6"</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b2beb3a..0ab31b3 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Дозволува апликацијата да ја менува вашата збирка на фотографии."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"да чита локации од вашата збирка на аудиовизуелни содржини"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Дозволува апликацијата да чита локации од вашата збирка на аудиовизуелни содржини."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрискиот хардвер е недостапен"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откриен е делумен отпечаток. Обидете се повторно."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатокот не можеше да се обработи. Обидете се повторно."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорот за отпечатоци е валкан. Исчистете го и обидете се повторно."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Прстот се движеше премногу бавно. Обидете се повторно."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Не е препознаено"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Непознат"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечатокот е проверен"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хардвер за отпечаток од прст не е достапен."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатокот не може да се складира. Отстранете го постоечкиот отпечаток."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 81421bb..eafd6a4 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"നിങ്ങളുടെ ഫോട്ടോ ശേഖരം പരിഷ്ക്കരിക്കുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുക"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"നിങ്ങളുടെ മീഡിയ ശേഖരത്തിൽ നിന്നും ലൊക്കേഷനുകൾ റീഡ് ചെയ്യുന്നതിന് ആപ്പിനെ അനുവദിക്കുന്നു."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ബയോമെട്രിക് ഹാർഡ്വെയർ ലഭ്യമല്ല"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"വിരലടയാളം ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"വിരലടയാളം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"വിരലടയാള സെൻസറിന് വൃത്തിയില്ല. അത് ശുചിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"വിരൽ വളരെ പതുക്കെ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"തിരിച്ചറിഞ്ഞില്ല"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"തിരിച്ചറിഞ്ഞില്ല"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിച്ചു"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ഫിംഗർപ്രിന്റ് ഹാർഡ്വെയർ ലഭ്യമല്ല."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"വിരലടയാളം സംഭരിക്കാനാവില്ല. നിലവിലുള്ള വിരലടയാളം നീക്കംചെയ്യുക."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index ddf1bc6..12e3186 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Таны зургийн цуглуулгыг тохируулах зөвшөөрлийг аппад олгодог."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"медиа цуглуулгаасаа байршлыг унших"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Таны медиа цуглуулгаас байршлыг унших зөвшөөрлийг аппад олгодог."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрийн техник хангамж боломжгүй байна"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Хурууны хээг дутуу уншуулсан байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Хурууны хээ боловсруулж чадахгүй байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Хурууны хээ мэдрэгч бохирдсон байна. Та цэвэрлэсний дараагаар дахин оролдоно уу."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Хуруу хэт удаан хөдөлгөсөн байна. Дахин оролдоно уу."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Танигдахгүй байна"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Таниагүй"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Хурууны хээг нотолсон"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хурууны хээний тоног төхөөрөмж бэлэн бус байна."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Хурууны хээг хадгалах боломжгүй байна. Одоо байгаа хурууны хээг арилгана уу."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index c257008..fe248c1 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"अॅपला तुमच्या फोटो संग्रहामध्ये सुधारणा करण्याची अनुमती देते."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"तुमच्या मीडिया संग्रहातून स्थाने वाचा"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"अॅपला तुमच्या मीडिया संग्रहामध्येील स्थाने वाचण्यासाठी अनुमती देते."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेअर उपलब्ध नाही"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फिंगरप्रिंट आढळली. कृपया पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फिंगरप्रिंटवर प्रक्रिया करणे शक्य झाले नाही. कृपया पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फिंगरप्रिंट सेन्सर खराब आहे. कृपया साफ करा आणि पुन्हा प्रयत्न करा."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"बोट खूप सावकाश हलविले. कृपया पुन्हा प्रयत्न करा."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"ओळखले नाही"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ओळखले नाही"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फिंगरप्रिंट ऑथेंटिकेट केली आहे"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फिंगरप्रिंट हार्डवेअर उपलब्ध नाही."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"फिंगरप्रिंट स्टोअर केले जाऊ शकत नाही. कृपया विद्यमान फिंगरप्रिंट काढा."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 939b9a4..a813a7b 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Membenarkan apl mengubah suai koleksi foto anda."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"baca lokasi daripada koleksi media anda"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Membenarkan apl membaca lokasi daripada koleksi media anda."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Perkakasan biometrik tidak tersedia"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Cap jari separa dikesan. Sila cuba lagi."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses cap jari. Sila cuba lagi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Penderia cap jari kotor. Sila bersihkan dan cuba lagi."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Jari digerakkan terlalu perlahan. Sila cuba lagi."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Tidak dicam"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tidak dikenali"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Cap jari disahkan"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Perkakasan cap jari tidak tersedia."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Cap jari tidak dapat disimpan. Sila alih keluar cap jari sedia ada."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 23a73ff..812ff75 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"အက်ပ်အား သင့်ဓာတ်ပုံစုစည်းမှုကို ပြုပြင်ခွင့်ပေးသည်။"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"သင့်မီဒီယာစုစည်းမှုမှ တည်နေရာများကို ဖတ်ခြင်း"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"အက်ပ်အား သင့်မီဒီယာစုစည်းမှုမှ တည်နေရာများကို ဖတ်ခွင့်ပေးသည်။"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ဇီဝအချက်အလက်သုံး ကွန်ပျူတာစက်ပစ္စည်း မရရှိနိုင်ပါ"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"လက်ဗွေရဦ တစ်ပိုင်းတစ်စ တွေ့ရှိသည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"လက်ဗွေရာယူခြင်း မဆောင်ရွက်နိုင်ပါ။ ထပ်မံကြိုးစားပါ။"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"လက်ဗွေရာဖတ်ကိရိယာ ညစ်ပေနေသည်။ ကျေးဇူးပြု၍ ရှင်းလင်းကာ ထပ်မံကြိုးစားပါ။"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"လက်ညှိုးအလွန်နှေးကွေးစွာ ရွေ့ခဲ့သည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"အသိအမှတ်မပြုပါ"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"မသိပါ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"လက်ဗွေကို အထောက်အထား စိစစ်ပြီးပါပြီ"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"လက်ဗွေရာ ဟာ့ဒ်ဝဲ မရနိုင်ပါ။"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"လက်ဗွေရာ သိုလှောင်၍မရပါ။ ကျေးဇူးပြု၍ ရှိပြီးလက်ဗွေရာအား ဖယ်ရှားပါ။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 7d3fe48..439b0de 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Lar appen gjøre endringer i bildesamlingen din."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"lese posisjoner fra mediesamlingen din"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Lar appen lese posisjoner fra mediesamlingen din."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk maskinvare er utilgjengelig"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Deler av fingeravtrykket er registrert. Prøv på nytt."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kunne ikke registrere fingeravtrykket. Prøv på nytt."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeravtrykksensoren er skitten. Rengjør den og prøv på nytt."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Du flyttet fingeren for sakte. Prøv på nytt."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ikke gjenkjent"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ikke gjenkjent"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeravtrykket er godkjent"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Maskinvare for fingeravtrykk er ikke tilgjengelig."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeravtrykket kan ikke lagres. Fjern et eksisterende fingeravtrykk."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 55e62ff..d45e28b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"यसले अनुप्रयोगलाई तपाईंको तस्बिरको सङ्ग्रह परिमार्जन गर्न दिन्छ।"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"आफ्नो मिडियाको सङ्ग्रहका स्थानहरू पढ्नुहोस्"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"यसले अनुप्रयोगलाई तपाईंको मिडिया सङ्ग्रहका स्थानहरू पढ्न दिन्छ।"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"बायोमेट्रिक हार्डवेयर उपलब्ध छैन"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक औठाछाप पत्ता लाग्यो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"औठाछाप प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"औँठाछाप सेन्सर फोहोर छ। कृपया सफा गरेर फेरि प्रयास गर्नुहोस्।"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"औंला निकै सुस्त सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"चिनिएको छैन"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"पहिचान भएन"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"फिंगरप्रिन्ट प्रमाणीकरण गरियो"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"औँठाछाप हार्डवेयर उपलब्ध छैन।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"औँठाछाप भण्डारण गर्न सकिँदैन। कृपया अवस्थित औठाछाप हटाउनुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 0e13132..df152b2 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Hiermee sta je de app toe je fotocollectie aan te passen."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"locaties van je mediacollecties bekijken"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Hiermee sta je de app toe locaties van je mediacollectie te bekijken."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrische hardware niet beschikbaar"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelijke vingerafdruk gedetecteerd. Probeer het opnieuw."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"De vingerafdruksensor moet worden schoongemaakt. Probeer het daarna opnieuw."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Vinger te langzaam bewogen. Probeer het opnieuw."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Niet herkend"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Niet herkend"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Vingerafdruk geverifieerd"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware voor vingerafdruk niet beschikbaar."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Vingerafdruk kan niet worden opgeslagen. Verwijder een bestaande vingerafdruk."</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index b3a2c81..78566da 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"ଆପଣଙ୍କ ଫଟୋ ସଂଗ୍ରହ ପରିବର୍ତ୍ତନ କରିବାକୁ ଆପ୍ ଅନୁମତି ଦେଇଥାଏ।"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ଆପଣଙ୍କ ମିଡିଆ ସଂଗ୍ରହ ଠାରୁ ଲୋକେସନ୍ଗୁଡିକୁ ପଢନ୍ତୁ"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ଆପଣଙ୍କ ମିଡିଆ ସଂଗ୍ରହ ଠାରୁ ଅବସ୍ଥାନଗୁଡିକୁ ପଢିବାକୁ ଆପ୍ ଅନୁମତି ଦେଇଥାଏ।"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ବାୟୋମେଟ୍ରିକ୍ ହାର୍ଡୱେର୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଆଂଶିକ ଚିହ୍ନଟ ହେଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପ୍ରୋସେସ୍ କରାଯାଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ସେନ୍ସର୍ ମଇଳା ହୋଇଯାଇଛି। ଦୟାକରି ସଫା କରନ୍ତୁ ଓ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ଆଙ୍ଗୁଠି ଖୁବ୍ ଧୀରେ ନିଆଗଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"ଚିହ୍ନଟ ହେଲା ନାହିଁ"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ଚିହ୍ନଟ ହେଲାନାହିଁ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପ୍ରମାଣୀକୃତ ହେଲା"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ହାର୍ଡୱେର୍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ଷ୍ଟୋର୍ କରାଯାଇପାରିବ ନାହିଁ। ଦୟାକରି ପୂର୍ବରୁ ଥିବା ଆଙ୍ଗୁଠି ଚିହ୍ନକୁ ବାହାର କରିଦିଅନ୍ତୁ।"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index d581e6b..bb38c88 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਟੋ ਸੰਗ੍ਰਹਿ ਨੂੰ ਸੋਧਣ ਦਿੰਦੀ ਹੈ।"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ਤੁਹਾਡੇ ਮੀਡੀਆ ਸੰਗ੍ਰਹਿ ਦੇ ਟਿਕਾਣਿਆਂ ਨੂੰ ਪੜ੍ਹਨਾ"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਮੀਡੀਆ ਸੰਗ੍ਰਹਿ ਦੇ ਟਿਕਾਣਿਆਂ ਨੂੰ ਪੜ੍ਹਨ ਦਿੰਦੀ ਹੈ।"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ਅਧੂਰਾ ਫਿੰਗਰਪ੍ਰਿਟ ਮਿਲਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕਰ ਸਕਿਆ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੰਵੇਦਕ ਗੰਦਾ ਹੈ। ਕਿਰਪਾ ਕਰਕੇ ਇਸਨੂੰ ਸਾਫ਼ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ਉਂਗਲ ਕਾਫ਼ੀ ਹੌਲੀ ਮੂਵ ਹੋਈ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ਪਛਾਣ ਨਹੀਂ ਹੋਈ"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਮਾਣਿਤ ਹੋਇਆ"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ।"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਸਕਦਾ। ਕਿਰਪਾ ਕਰਕੇ ਇੱਕ ਮੌਜੂਦਾ ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਟਾਓ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6f56e7f..d59b138 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Zezwala aplikacji na modyfikowanie kolekcji zdjęć."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"odczytywanie lokalizacji z kolekcji multimediów"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Zezwala aplikacji na odczytywanie lokalizacji z kolekcji multimediów."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Sprzęt biometryczny niedostępny"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Odcisk palca został odczytany tylko częściowo. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Czytnik linii papilarnych jest zabrudzony. Wyczyść go i spróbuj ponownie."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Palec został obrócony zbyt wolno. Spróbuj ponownie."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nie rozpoznano odcisku palca."</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nie rozpoznano"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Uwierzytelniono odciskiem palca"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Czytnik linii papilarnych nie jest dostępny."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Nie można zapisać odcisku palca. Usuń istniejący odcisk palca."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index ad68dde..48302a0 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -47,12 +47,12 @@
<string name="mismatchPin" msgid="609379054496863419">"Os PINs digitados não correspondem."</string>
<string name="invalidPin" msgid="3850018445187475377">"Digite um PIN com 4 a 8 números."</string>
<string name="invalidPuk" msgid="8761456210898036513">"Digite um PUK com oito números ou mais."</string>
- <string name="needPuk" msgid="919668385956251611">"O seu cartão SIM está bloqueado por um PUK. Digite o código PUK para desbloqueá-lo."</string>
- <string name="needPuk2" msgid="4526033371987193070">"Digite o PUK2 para desbloquear o cartão SIM."</string>
- <string name="enablePin" msgid="209412020907207950">"Falha. Ative o bloqueio do SIM/R-UIM."</string>
+ <string name="needPuk" msgid="919668385956251611">"O seu chip está bloqueado por um PUK. Digite o código PUK para desbloqueá-lo."</string>
+ <string name="needPuk2" msgid="4526033371987193070">"Digite o PUK2 para desbloquear o chip."</string>
+ <string name="enablePin" msgid="209412020907207950">"Falha. Ative o bloqueio do chip/R-UIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
- <item quantity="one">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o SIM será bloqueado.</item>
- <item quantity="other">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o SIM será bloqueado.</item>
+ <item quantity="one">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
+ <item quantity="other">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
@@ -82,7 +82,7 @@
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Sem serviço de voz"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="8037246983606545202">"Nenhum serviço de voz nem chamada de emergência"</string>
<string name="RestrictedStateContent" msgid="6538703255570997248">"Temporariamente desativado pela sua operadora"</string>
- <string name="RestrictedStateContentMsimTemplate" msgid="673416791370248176">"Temporariamente desativado pela sua operadora para o SIM <xliff:g id="SIMNUMBER">%d</xliff:g>"</string>
+ <string name="RestrictedStateContentMsimTemplate" msgid="673416791370248176">"Temporariamente desativado pela sua operadora para o chip <xliff:g id="SIMNUMBER">%d</xliff:g>"</string>
<string name="NetworkPreferenceSwitchTitle" msgid="6982395015324165258">"Não foi possível acessar a rede móvel"</string>
<string name="NetworkPreferenceSwitchSummary" msgid="509327194863482733">"Tente alterar a rede preferencial. Toque para alterar."</string>
<string name="EmergencyCallWarningTitle" msgid="813380189532491336">"Chamadas de emergência indisponíveis"</string>
@@ -94,7 +94,7 @@
<string name="notification_channel_sms" msgid="3441746047346135073">"Mensagens SMS"</string>
<string name="notification_channel_voice_mail" msgid="3954099424160511919">"Mensagens do correio de voz"</string>
<string name="notification_channel_wfc" msgid="2130802501654254801">"Chamadas por Wi-Fi"</string>
- <string name="notification_channel_sim" msgid="4052095493875188564">"Status do SIM"</string>
+ <string name="notification_channel_sim" msgid="4052095493875188564">"Status do chip"</string>
<string name="peerTtyModeFull" msgid="6165351790010341421">"TTD modo COMPLETO solicitado"</string>
<string name="peerTtyModeHco" msgid="5728602160669216784">"TTD modo HCO solicitado"</string>
<string name="peerTtyModeVco" msgid="1742404978686538049">"TTD modo VCO solicitado"</string>
@@ -420,8 +420,8 @@
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
<string name="permdesc_recordAudio" msgid="4245930455135321433">"Este app pode gravar áudio usando o microfone a qualquer momento."</string>
- <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o SIM"</string>
- <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que o app envie comandos ao SIM. Muito perigoso."</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o chip"</string>
+ <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que o app envie comandos ao chip. Muito perigoso."</string>
<string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e gravar vídeos"</string>
<string name="permdesc_camera" msgid="5392231870049240670">"Este app pode tirar fotos e gravar vídeos usando a câmera a qualquer momento."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que o app modifique sua coleção de fotos."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ler locais na sua coleção de mídias"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que o app leia os locais na sua coleção de mídias."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital parcial detectada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressão digital está sujo. Limpe-o e tente novamente."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"O movimento do dedo está muito lento. Tente novamente."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Não reconhecido"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Impressão digital autenticada"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não foi possível armazenar a impressão digital. Remova uma impressão digital já existente."</string>
@@ -795,14 +796,14 @@
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Tente novamente"</string>
<string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbloqueio para todos os recursos e dados"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
- <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Sem cartão SIM"</string>
- <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Não há um cartão SIM no tablet."</string>
- <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Nenhum cartão SIM na TV."</string>
- <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Não há um cartão SIM no telefone."</string>
- <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Insera um cartão SIM."</string>
- <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"O cartão SIM não foi inserido ou não é possível lê-lo. Insira um cartão SIM."</string>
- <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Cartão SIM inutilizável."</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"O cartão SIM foi desativado permanentemente.\nEntre em contato com seu provedor de serviços sem fio para obter outro cartão SIM."</string>
+ <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Sem chip"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Não há um chip no tablet."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Nenhum chip na TV."</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Não há um chip no telefone."</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Insera um chip."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"O chip não foi inserido ou não é possível lê-lo. Insira um chip."</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Chip inutilizável."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"O chip foi desativado permanentemente.\nEntre em contato com seu provedor de serviços sem fio para obter outro chip."</string>
<string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Faixa anterior"</string>
<string name="lockscreen_transport_next_description" msgid="573285210424377338">"Próxima faixa"</string>
<string name="lockscreen_transport_pause_description" msgid="3980308465056173363">"Pausar"</string>
@@ -812,10 +813,10 @@
<string name="lockscreen_transport_ffw_description" msgid="42987149870928985">"Avançar"</string>
<string name="emergency_calls_only" msgid="6733978304386365407">"Só chamadas de emergência"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"Rede bloqueada"</string>
- <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"O cartão SIM está bloqueado pelo PUK."</string>
+ <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"O chip está bloqueado pelo PUK."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulte o Guia do usuário ou entre em contato com o Serviço de atendimento ao cliente."</string>
- <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O cartão SIM está bloqueado."</string>
- <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando o cartão SIM…"</string>
+ <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O chip está bloqueado."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando o chip…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes.\n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
@@ -866,8 +867,8 @@
<string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueio com padrão."</string>
<string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueio facial."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueio com PIN."</string>
- <string name="keyguard_accessibility_sim_pin_unlock" msgid="9149698847116962307">"Desbloqueio com PIN do SIM."</string>
- <string name="keyguard_accessibility_sim_puk_unlock" msgid="9106899279724723341">"Desbloqueio com PUK do SIM."</string>
+ <string name="keyguard_accessibility_sim_pin_unlock" msgid="9149698847116962307">"Desbloqueio com PIN do chip."</string>
+ <string name="keyguard_accessibility_sim_puk_unlock" msgid="9106899279724723341">"Desbloqueio com PUK do chip."</string>
<string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueio com senha."</string>
<string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área do padrão."</string>
<string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslize."</string>
@@ -1277,17 +1278,17 @@
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"P/ alterar: Configurações > Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"Sempre permitir"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"Nunca permitir"</string>
- <string name="sim_removed_title" msgid="6227712319223226185">"Cartão SIM removido"</string>
- <string name="sim_removed_message" msgid="2333164559970958645">"A rede móvel ficará indisponível até que você reinicie com um cartão SIM válido inserido."</string>
+ <string name="sim_removed_title" msgid="6227712319223226185">"Chip removido"</string>
+ <string name="sim_removed_message" msgid="2333164559970958645">"A rede móvel ficará indisponível até que você reinicie com um chip válido inserido."</string>
<string name="sim_done_button" msgid="827949989369963775">"Concluído"</string>
- <string name="sim_added_title" msgid="3719670512889674693">"Cartão SIM adicionado"</string>
+ <string name="sim_added_title" msgid="3719670512889674693">"Chip adicionado"</string>
<string name="sim_added_message" msgid="6599945301141050216">"Reinicie o dispositivo para acessar a rede móvel."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"Reiniciar"</string>
<string name="install_carrier_app_notification_title" msgid="9056007111024059888">"Ativar serviço móvel"</string>
- <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"Faça o download do app da operadora para ativar seu novo SIM"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"Faça o download do app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo SIM"</string>
+ <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"Faça o download do app da operadora para ativar seu novo chip"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"Faça o download do app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
<string name="install_carrier_app_notification_button" msgid="3094206295081900849">"Fazer download do app"</string>
- <string name="carrier_app_notification_title" msgid="8921767385872554621">"Novo SIM inserido"</string>
+ <string name="carrier_app_notification_title" msgid="8921767385872554621">"Novo chip inserido"</string>
<string name="carrier_app_notification_text" msgid="1132487343346050225">"Toque para configurar"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Definir hora"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Definir data"</string>
@@ -1572,17 +1573,17 @@
<item quantity="other">Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe seu padrão"</string>
- <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Digite o PIN do cartão SIM"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Digite o PIN do chip"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Digite o PIN"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"Digite a senha"</string>
- <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"O SIM foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para obter mais detalhes."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"O chip foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para obter mais detalhes."</string>
<string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Digite o código PIN desejado"</string>
<string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirme o código PIN desejado"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando o cartão SIM…"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando o chip…"</string>
<string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorreto."</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Digite um PIN com quatro a oito números."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"O código PUK deve ter oito números."</string>
- <string name="kg_invalid_puk" msgid="3638289409676051243">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o SIM."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o chip."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Os códigos PIN não coincidem"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Muitas tentativas de padrão"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, faça login usando sua Conta do Google."</string>
@@ -1910,9 +1911,9 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Teste de mensagens de emergência"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM não autorizado para voz"</string>
- <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM não aprovisionado para voz"</string>
- <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM não autorizado para voz"</string>
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Chip não autorizado para voz"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"Chip não aprovisionado para voz"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"Chip não autorizado para voz"</string>
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Smartphone não autorizado para voz"</string>
<string name="mmcc_authentication_reject_msim_template" msgid="1217031195834766479">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> não permitido"</string>
<string name="mmcc_imsi_unknown_in_hlr_msim_template" msgid="5636464607596778986">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> não aprovisionado"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 631e9f3..3f02828 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que a aplicação modifique a sua coleção de fotos."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ler as localizações a partir da sua coleção de multimédia"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que a aplicação leia as localizações a partir da sua coleção de multimédia."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível."</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital detetada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressões digitais está sujo. Limpe-o e tente novamente."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Moveu o dedo demasiado lentamente. Tente novamente."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Não reconhecido"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido."</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"A impressão digital foi autenticada."</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não é possível armazenar a impressão digital. Remova uma impressão digital existente."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index ad68dde..48302a0 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -47,12 +47,12 @@
<string name="mismatchPin" msgid="609379054496863419">"Os PINs digitados não correspondem."</string>
<string name="invalidPin" msgid="3850018445187475377">"Digite um PIN com 4 a 8 números."</string>
<string name="invalidPuk" msgid="8761456210898036513">"Digite um PUK com oito números ou mais."</string>
- <string name="needPuk" msgid="919668385956251611">"O seu cartão SIM está bloqueado por um PUK. Digite o código PUK para desbloqueá-lo."</string>
- <string name="needPuk2" msgid="4526033371987193070">"Digite o PUK2 para desbloquear o cartão SIM."</string>
- <string name="enablePin" msgid="209412020907207950">"Falha. Ative o bloqueio do SIM/R-UIM."</string>
+ <string name="needPuk" msgid="919668385956251611">"O seu chip está bloqueado por um PUK. Digite o código PUK para desbloqueá-lo."</string>
+ <string name="needPuk2" msgid="4526033371987193070">"Digite o PUK2 para desbloquear o chip."</string>
+ <string name="enablePin" msgid="209412020907207950">"Falha. Ative o bloqueio do chip/R-UIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
- <item quantity="one">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o SIM será bloqueado.</item>
- <item quantity="other">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o SIM será bloqueado.</item>
+ <item quantity="one">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
+ <item quantity="other">Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip será bloqueado.</item>
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
@@ -82,7 +82,7 @@
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Sem serviço de voz"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="8037246983606545202">"Nenhum serviço de voz nem chamada de emergência"</string>
<string name="RestrictedStateContent" msgid="6538703255570997248">"Temporariamente desativado pela sua operadora"</string>
- <string name="RestrictedStateContentMsimTemplate" msgid="673416791370248176">"Temporariamente desativado pela sua operadora para o SIM <xliff:g id="SIMNUMBER">%d</xliff:g>"</string>
+ <string name="RestrictedStateContentMsimTemplate" msgid="673416791370248176">"Temporariamente desativado pela sua operadora para o chip <xliff:g id="SIMNUMBER">%d</xliff:g>"</string>
<string name="NetworkPreferenceSwitchTitle" msgid="6982395015324165258">"Não foi possível acessar a rede móvel"</string>
<string name="NetworkPreferenceSwitchSummary" msgid="509327194863482733">"Tente alterar a rede preferencial. Toque para alterar."</string>
<string name="EmergencyCallWarningTitle" msgid="813380189532491336">"Chamadas de emergência indisponíveis"</string>
@@ -94,7 +94,7 @@
<string name="notification_channel_sms" msgid="3441746047346135073">"Mensagens SMS"</string>
<string name="notification_channel_voice_mail" msgid="3954099424160511919">"Mensagens do correio de voz"</string>
<string name="notification_channel_wfc" msgid="2130802501654254801">"Chamadas por Wi-Fi"</string>
- <string name="notification_channel_sim" msgid="4052095493875188564">"Status do SIM"</string>
+ <string name="notification_channel_sim" msgid="4052095493875188564">"Status do chip"</string>
<string name="peerTtyModeFull" msgid="6165351790010341421">"TTD modo COMPLETO solicitado"</string>
<string name="peerTtyModeHco" msgid="5728602160669216784">"TTD modo HCO solicitado"</string>
<string name="peerTtyModeVco" msgid="1742404978686538049">"TTD modo VCO solicitado"</string>
@@ -420,8 +420,8 @@
<string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"Permite que o app modifique configurações de áudio globais como volume e alto-falantes de saída."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
<string name="permdesc_recordAudio" msgid="4245930455135321433">"Este app pode gravar áudio usando o microfone a qualquer momento."</string>
- <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o SIM"</string>
- <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que o app envie comandos ao SIM. Muito perigoso."</string>
+ <string name="permlab_sim_communication" msgid="2935852302216852065">"enviar comandos para o chip"</string>
+ <string name="permdesc_sim_communication" msgid="5725159654279639498">"Permite que o app envie comandos ao chip. Muito perigoso."</string>
<string name="permlab_camera" msgid="3616391919559751192">"tirar fotos e gravar vídeos"</string>
<string name="permdesc_camera" msgid="5392231870049240670">"Este app pode tirar fotos e gravar vídeos usando a câmera a qualquer momento."</string>
<string name="permlab_vibrate" msgid="7696427026057705834">"controlar vibração"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite que o app modifique sua coleção de fotos."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ler locais na sua coleção de mídias"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite que o app leia os locais na sua coleção de mídias."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biométrico indisponível"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital parcial detectada. Tente novamente."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressão digital está sujo. Limpe-o e tente novamente."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"O movimento do dedo está muito lento. Tente novamente."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Não reconhecido"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Não reconhecido"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Impressão digital autenticada"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não foi possível armazenar a impressão digital. Remova uma impressão digital já existente."</string>
@@ -795,14 +796,14 @@
<string name="lockscreen_password_wrong" msgid="5737815393253165301">"Tente novamente"</string>
<string name="lockscreen_storage_locked" msgid="9167551160010625200">"Desbloqueio para todos os recursos e dados"</string>
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"O número máximo de tentativas de Desbloqueio por reconhecimento facial foi excedido"</string>
- <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Sem cartão SIM"</string>
- <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Não há um cartão SIM no tablet."</string>
- <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Nenhum cartão SIM na TV."</string>
- <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Não há um cartão SIM no telefone."</string>
- <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Insera um cartão SIM."</string>
- <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"O cartão SIM não foi inserido ou não é possível lê-lo. Insira um cartão SIM."</string>
- <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Cartão SIM inutilizável."</string>
- <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"O cartão SIM foi desativado permanentemente.\nEntre em contato com seu provedor de serviços sem fio para obter outro cartão SIM."</string>
+ <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Sem chip"</string>
+ <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Não há um chip no tablet."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Nenhum chip na TV."</string>
+ <string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Não há um chip no telefone."</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Insera um chip."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"O chip não foi inserido ou não é possível lê-lo. Insira um chip."</string>
+ <string name="lockscreen_permanent_disabled_sim_message_short" msgid="5096149665138916184">"Chip inutilizável."</string>
+ <string name="lockscreen_permanent_disabled_sim_instructions" msgid="910904643433151371">"O chip foi desativado permanentemente.\nEntre em contato com seu provedor de serviços sem fio para obter outro chip."</string>
<string name="lockscreen_transport_prev_description" msgid="6300840251218161534">"Faixa anterior"</string>
<string name="lockscreen_transport_next_description" msgid="573285210424377338">"Próxima faixa"</string>
<string name="lockscreen_transport_pause_description" msgid="3980308465056173363">"Pausar"</string>
@@ -812,10 +813,10 @@
<string name="lockscreen_transport_ffw_description" msgid="42987149870928985">"Avançar"</string>
<string name="emergency_calls_only" msgid="6733978304386365407">"Só chamadas de emergência"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"Rede bloqueada"</string>
- <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"O cartão SIM está bloqueado pelo PUK."</string>
+ <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"O chip está bloqueado pelo PUK."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulte o Guia do usuário ou entre em contato com o Serviço de atendimento ao cliente."</string>
- <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O cartão SIM está bloqueado."</string>
- <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando o cartão SIM…"</string>
+ <string name="lockscreen_sim_locked_message" msgid="8066660129206001039">"O chip está bloqueado."</string>
+ <string name="lockscreen_sim_unlock_progress_dialog_message" msgid="595323214052881264">"Desbloqueando o chip…"</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6481623830344107222">"Você desenhou seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Você digitou sua senha incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes.\n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
@@ -866,8 +867,8 @@
<string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueio com padrão."</string>
<string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueio facial."</string>
<string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"Desbloqueio com PIN."</string>
- <string name="keyguard_accessibility_sim_pin_unlock" msgid="9149698847116962307">"Desbloqueio com PIN do SIM."</string>
- <string name="keyguard_accessibility_sim_puk_unlock" msgid="9106899279724723341">"Desbloqueio com PUK do SIM."</string>
+ <string name="keyguard_accessibility_sim_pin_unlock" msgid="9149698847116962307">"Desbloqueio com PIN do chip."</string>
+ <string name="keyguard_accessibility_sim_puk_unlock" msgid="9106899279724723341">"Desbloqueio com PUK do chip."</string>
<string name="keyguard_accessibility_password_unlock" msgid="7675777623912155089">"Desbloqueio com senha."</string>
<string name="keyguard_accessibility_pattern_area" msgid="7679891324509597904">"Área do padrão."</string>
<string name="keyguard_accessibility_slide_area" msgid="6736064494019979544">"Área de deslize."</string>
@@ -1277,17 +1278,17 @@
<string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"P/ alterar: Configurações > Apps"</string>
<string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"Sempre permitir"</string>
<string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"Nunca permitir"</string>
- <string name="sim_removed_title" msgid="6227712319223226185">"Cartão SIM removido"</string>
- <string name="sim_removed_message" msgid="2333164559970958645">"A rede móvel ficará indisponível até que você reinicie com um cartão SIM válido inserido."</string>
+ <string name="sim_removed_title" msgid="6227712319223226185">"Chip removido"</string>
+ <string name="sim_removed_message" msgid="2333164559970958645">"A rede móvel ficará indisponível até que você reinicie com um chip válido inserido."</string>
<string name="sim_done_button" msgid="827949989369963775">"Concluído"</string>
- <string name="sim_added_title" msgid="3719670512889674693">"Cartão SIM adicionado"</string>
+ <string name="sim_added_title" msgid="3719670512889674693">"Chip adicionado"</string>
<string name="sim_added_message" msgid="6599945301141050216">"Reinicie o dispositivo para acessar a rede móvel."</string>
<string name="sim_restart_button" msgid="4722407842815232347">"Reiniciar"</string>
<string name="install_carrier_app_notification_title" msgid="9056007111024059888">"Ativar serviço móvel"</string>
- <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"Faça o download do app da operadora para ativar seu novo SIM"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"Faça o download do app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo SIM"</string>
+ <string name="install_carrier_app_notification_text" msgid="3346681446158696001">"Faça o download do app da operadora para ativar seu novo chip"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="1196505084835248137">"Faça o download do app <xliff:g id="APP_NAME">%1$s</xliff:g> para ativar seu novo chip"</string>
<string name="install_carrier_app_notification_button" msgid="3094206295081900849">"Fazer download do app"</string>
- <string name="carrier_app_notification_title" msgid="8921767385872554621">"Novo SIM inserido"</string>
+ <string name="carrier_app_notification_title" msgid="8921767385872554621">"Novo chip inserido"</string>
<string name="carrier_app_notification_text" msgid="1132487343346050225">"Toque para configurar"</string>
<string name="time_picker_dialog_title" msgid="8349362623068819295">"Definir hora"</string>
<string name="date_picker_dialog_title" msgid="5879450659453782278">"Definir data"</string>
@@ -1572,17 +1573,17 @@
<item quantity="other">Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe seu padrão"</string>
- <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Digite o PIN do cartão SIM"</string>
+ <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Digite o PIN do chip"</string>
<string name="kg_pin_instructions" msgid="2377242233495111557">"Digite o PIN"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"Digite a senha"</string>
- <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"O SIM foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para obter mais detalhes."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"O chip foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para obter mais detalhes."</string>
<string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Digite o código PIN desejado"</string>
<string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Confirme o código PIN desejado"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando o cartão SIM…"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Desbloqueando o chip…"</string>
<string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Código PIN incorreto."</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Digite um PIN com quatro a oito números."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"O código PUK deve ter oito números."</string>
- <string name="kg_invalid_puk" msgid="3638289409676051243">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o SIM."</string>
+ <string name="kg_invalid_puk" msgid="3638289409676051243">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o chip."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"Os códigos PIN não coincidem"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Muitas tentativas de padrão"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"Para desbloquear, faça login usando sua Conta do Google."</string>
@@ -1910,9 +1911,9 @@
<string name="etws_primary_default_message_test" msgid="2709597093560037455">"Teste de mensagens de emergência"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
- <string name="mmcc_authentication_reject" msgid="5767701075994754356">"SIM não autorizado para voz"</string>
- <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"SIM não aprovisionado para voz"</string>
- <string name="mmcc_illegal_ms" msgid="807334478177362062">"SIM não autorizado para voz"</string>
+ <string name="mmcc_authentication_reject" msgid="5767701075994754356">"Chip não autorizado para voz"</string>
+ <string name="mmcc_imsi_unknown_in_hlr" msgid="5316658473301462825">"Chip não aprovisionado para voz"</string>
+ <string name="mmcc_illegal_ms" msgid="807334478177362062">"Chip não autorizado para voz"</string>
<string name="mmcc_illegal_me" msgid="1950705155760872972">"Smartphone não autorizado para voz"</string>
<string name="mmcc_authentication_reject_msim_template" msgid="1217031195834766479">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> não permitido"</string>
<string name="mmcc_imsi_unknown_in_hlr_msim_template" msgid="5636464607596778986">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> não aprovisionado"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index efd68ac..1c95559 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -517,6 +517,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Permite aplicației să vă modifice colecția de fotografii."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"citiți locațiile din colecția media"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Permite aplicației să citească locațiile din colecția dvs. media."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Hardware biometric indisponibil"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"S-a detectat parțial amprenta. Încercați din nou."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Amprenta nu a putut fi procesată. Încercați din nou."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzorul pentru amprente este murdar. Curățați-l și încercați din nou."</string>
@@ -524,7 +525,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Ați mișcat degetul prea lent. Încercați din nou."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nu este recunoscută"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nu este recunoscut"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Amprentă autentificată"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardware-ul pentru amprentă nu este disponibil."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Amprenta nu poate fi stocată. Eliminați o amprentă existentă."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 6cc870c..e31f161 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Приложение сможет вносить изменения в вашу фотоколлекцию."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"доступ к геоданным в медиаколлекции"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Приложение получит доступ к геоданным в вашей медиаколлекции."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометрическое оборудование недоступно"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Отсканирована только часть пальца. Повторите попытку."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не удалось распознать отпечаток. Повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Очистите сканер и повторите попытку."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Вы перемещали палец слишком медленно. Повторите попытку."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Не распознано"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Не распознано"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отпечаток пальца проверен"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Сканер недоступен"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Чтобы сохранить новый отпечаток, удалите существующий."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index c263589..8b89a97 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -514,6 +514,8 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"ඔබගේ ඡායාරූප එකතුව වෙනස් කිරීමට යෙදුමට ඉඩ දෙයි."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"ඔබගේ මාධ්ය එකතුවෙන් ස්ථාන කියවන්න"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ඔබගේ මාධ්ය එකතුවෙන් ස්ථාන කියවීමට යෙදුමට ඉඩ දෙයි."</string>
+ <!-- no translation found for biometric_error_hw_unavailable (645781226537551036) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ඇඟිලි සලකුණ අඩ වශයෙන් අනාවරණය කර ගැනිණි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ඇඟිලි සලකුණු සංවේදකය අපිරිසිදුයි. කරුණාකර පිරිසිදු කර නැවත උත්සාහ කරන්න."</string>
@@ -521,7 +523,8 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"ඇඟිල්ල වඩා සෙමෙන් ගෙන යන ලදි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"අඳුනාගත නොහැක"</string>
+ <!-- no translation found for biometric_not_recognized (5770511773560736082) -->
+ <skip />
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ඇඟිලි සලකුණ සත්යාපනය කරන ලදී"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැකිය."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ඇඟිලි සලකුණ ගබඩා කළ නොහැක. දැනට පවතින ඇඟිලි සලකුණක් ඉවත් කරන්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f3569c9..3109702 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Umožňuje aplikácii upravovať zbierku fotiek."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"čítať polohy zo zbierky médií"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Umožňuje aplikácii čítať polohy zo zbierky médií."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrický hardvér nie je k dispozícii"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Podarilo sa rozpoznať iba časť odtlačku prsta. Skúste to znova."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Odtlačok prsta sa nepodarilo spracovať. Skúste to znova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Snímač odtlačkov je špinavý. Vyčistite ho a skúste to znova."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Pohli ste prstom príliš pomaly. Skúste to znova."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nebol rozpoznaný"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nerozpoznané"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Odtlačok bol overený"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardvér na snímanie odtlačku prsta nie je k dispozícii"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Odtlačok prsta nie je možné uložiť. Odstráňte existujúci odtlačok."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 0f9d038..4575d00 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Aplikaciji omogoča spreminjanje zbirke fotografij."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"branje lokacij v predstavnostni zbirki"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Aplikaciji omogoča branje lokacij v predstavnostni zbirki."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Strojna oprema za biometrične podatke ni na voljo"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Zaznan delni prstni odtis. Poskusite znova."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Prstnega odtisa ni bilo mogoče obdelati. Poskusite znova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Tipalo prstnih odtisov je umazano. Očistite ga in poskusite znova."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Prepočasen premik prsta. Poskusite znova."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ni prepoznano"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Ni prepoznano"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Pristnost prstnega odtisa je preverjena"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Strojna oprema za prstne odtise ni na voljo."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Prstnega odtisa ni mogoče shraniti. Odstranite obstoječi prstni odtis."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 17ec70d..1149421 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -284,7 +284,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të dërgojë dhe të shikojë mesazhet SMS?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Hapësira e ruajtjes"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"qasjen te fotografitë, përmbajtjet audio-vizuale dhe skedarët në pajisje"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të ketë qasje te fotografitë, media dhe skedarët në pajisjen tënde?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"Të lejohet apl. <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> që të ketë qasje te fotografitë, media dhe skedarët në pajisjen tënde?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofoni"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"regjistro audio"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"Të lejohet që <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> të regjistrojë audio?"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Lejon aplikacionin të modifikojë koleksionin tënd të fotografive."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"lexo vendndodhjet nga koleksioni yt i medias"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Lejon aplikacionin të lexojë vendndodhjet nga koleksioni yt i medias."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Nuk ofrohet harduer biometrik"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"U zbulua një gjurmë gishti e pjesshme. Provo përsëri."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Gjurma e gishtit nuk mund të përpunohej. Provo përsëri."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensori i gjurmës së gishtit nuk është i pastër. Pastroje dhe provo përsëri."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Gishti lëvizi shumë ngadalë. Provo përsëri."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Nuk njihet"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Nuk njihet"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Gjurma e gishtit u vërtetua"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hardueri i gjurmës së gishtit nuk mundësohet."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Gjurma e gishtit nuk mund të ruhet. Hiq një gjurmë gishti ekzistuese."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 517815d..7536531 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -517,6 +517,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Дозвољава апликацији да мења колекцију слика."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"читање локација из медијске колекције"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Дозвољава апликацији да чита локације из медијске колекције."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Биометријски хардвер није доступан"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откривен је делимични отисак прста. Пробајте поново."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Није успела обрада отиска прста. Пробајте поново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензор за отиске прстију је прљав. Очистите га и покушајте поново."</string>
@@ -524,7 +525,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Превише споро сте померили прст. Пробајте поново."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Није препознат"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Није препознато"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Отисак прста је потврђен"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Хардвер за отиске прстију није доступан."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Није могуће сачувати отисак прста. Уклоните неки од постојећих отисака прстију."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 835f198..31bb349 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Tillåter att appen gör ändringar i din fotosamling."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"läsa av platser i din mediesamling"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Tillåter att appen läser av platser i din mediesamling."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrisk maskinvara är inte tillgänglig"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Ofullständigt fingeravtryck. Försök igen."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeravtryckssensorn är smutsig. Rengör den och försök igen."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Du rörde fingret för långsamt. Försök igen."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Identifierades inte"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Identifierades inte"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Fingeravtrycket har autentiserats"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Det finns ingen maskinvara för fingeravtryck."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeravtrycket kan inte lagras. Ta bort ett befintligt fingeravtryck."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index c846f08..3d26224 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -282,7 +282,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> itume na ione ujumbe wa SMS?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Hifadhi"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"ifikie picha, maudhui na faili kwenye kifaa chako"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie picha, maudhui na faili kwenye kifaa chako?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"Ungependa <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ifikie picha, maudhui na faili kwenye kifaa chako?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Kipokea sauti"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"irekodi sauti"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"Ungependa kuiruhusu <b><xliff:g id="APP_NAME">%1$s</xliff:g></b> irekodi sauti?"</string>
@@ -512,6 +512,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Inaruhusu programu kubadilisha mkusanyiko wa picha zako."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"kusoma maeneo kwenye mkusanyiko wa vipengee vyako"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Inaruhusu programu kusoma maeneo kwenye mkusanyiko wa vipengee vyako."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Maunzi ya bayometriki hayapatikani"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Kitambuzi kimegundua sehemu ya kitambulisho. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Haikuweza kuchakata kitambulisho. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Kitambuzi alama ya kidole ni kichafu. Tafadhali kisafishe na ujaribu tena."</string>
@@ -519,7 +520,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Kidole kilisogezwa polepole zaidi. Tafadhali jaribu tena."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Haitambuliwi"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Hayatambuliki"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Imethibitisha alama ya kidole"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Maunzi ya kitambulisho hayapatikani."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Kitambulisho hakiwezi kuhifadhiwa. Tafadhali ondoa kitambulisho kilichopo."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index ba58742..b175f75 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -514,6 +514,8 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"உங்களின் படத் தொகுப்பை மாற்ற ஆப்ஸை அனுமதிக்கும்."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"மீடியா தொகுப்பிலிருந்து இடங்களை அறிதல்"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"உங்களின் மீடியா தொகுப்பிலிருந்து இடங்களை அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கும்."</string>
+ <!-- no translation found for biometric_error_hw_unavailable (645781226537551036) -->
+ <skip />
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"கைரேகையை ஓரளவுதான் கண்டறிய முடிந்தது. மீண்டும் முயலவும்."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"கைரேகை உணர்வியில் தூசி உள்ளது. சுத்தம் செய்து, முயலவும்."</string>
@@ -521,7 +523,8 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"விரலை மிகவும் மெதுவாக நகர்த்திவிட்டீர்கள். மீண்டும் முயற்சிக்கவும்."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"அறியப்படவில்லை"</string>
+ <!-- no translation found for biometric_not_recognized (5770511773560736082) -->
+ <skip />
<string name="fingerprint_authenticated" msgid="5309333983002526448">"கைரேகை அங்கீகரிக்கப்பட்டது"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"கைரேகை வன்பொருள் இல்லை."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"கைரேகையைச் சேமிக்க முடியவில்லை. ஏற்கனவே உள்ள கைரேகையை அகற்றவும்."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index b0d7a4dc..2027d33 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"మీ ఫోటో సేకరణను సవరించడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవండి"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"మీ మీడియా సేకరణ నుండి స్థానాలను చదవడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"బయోమెట్రిక్ హార్డ్వేర్ అందుబాటులో లేదు"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"పాక్షిక వేలిముద్ర గుర్తించబడింది. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"వేలిముద్ర సెన్సార్ మురికిగా ఉంది. దయచేసి శుభ్రపరిచి, మళ్లీ ప్రయత్నించండి."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"వేలిని చాలా నెమ్మదిగా కదిలించారు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"గుర్తించలేదు"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"గుర్తించలేదు"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"వేలిముద్ర ప్రమాణీకరించబడింది"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"వేలిముద్ర హార్డ్వేర్ అందుబాటులో లేదు."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"వేలిముద్రను నిల్వ చేయడం సాధ్యపడదు. దయచేసి ఇప్పటికే ఉన్న వేలిముద్రను తీసివేయండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 07107cb..84fd018 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"อนุญาตให้แอปแก้ไขคอลเล็กชันรูปภาพของคุณ"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"อ่านตำแหน่งจากคอลเล็กชันสื่อของคุณ"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"อนุญาตให้แอปอ่านตำแหน่งจากคอลเล็กชันสื่อของคุณ"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"ฮาร์ดแวร์ไบโอเมตริกไม่พร้อมใช้งาน"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"ตรวจพบลายนิ้วมือเพียงบางส่วน โปรดลองอีกครั้ง"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ไม่สามารถประมวลผลลายนิ้วมือได้ โปรดลองอีกครั้ง"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"เซ็นเซอร์ลายนิ้วมือไม่สะอาด โปรดทำความสะอาดและลองอีกครั้ง"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"นิ้วเคลื่อนที่ช้าเกินไป โปรดลองอีกครั้ง"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"ไม่รู้จัก"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"ไม่รู้จัก"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"ตรวจสอบสิทธิ์ลายนิ้วมือแล้ว"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"ฮาร์ดแวร์ลายนิ้วมือไม่พร้อมใช้งาน"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"ไม่สามารถเก็บลายนิ้วมือได้ โปรดนำลายนิ้วมือที่มีอยู่ออก"</string>
@@ -1509,7 +1510,7 @@
<string name="data_usage_mobile_limit_snoozed_title" msgid="3171402244827034372">"เกินปริมาณเน็ตมือถือที่กำหนดไว้"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="3547771791046344188">"เกินขีดจำกัดของข้อมูล Wi-Fi"</string>
<string name="data_usage_limit_snoozed_body" msgid="1671222777207603301">"คุณใช้อินเทอร์เน็ตเกินไป <xliff:g id="SIZE">%s</xliff:g> จากปริมาณที่กำหนดไว้"</string>
- <string name="data_usage_restricted_title" msgid="5965157361036321914">"จำกัดข้อมูลแบ็กกราวด์"</string>
+ <string name="data_usage_restricted_title" msgid="5965157361036321914">"จำกัดอินเทอร์เน็ตที่ใช้งานอยู่เบื้องหลัง"</string>
<string name="data_usage_restricted_body" msgid="469866376337242726">"แตะเพื่อนำข้อจำกัดออก"</string>
<string name="data_usage_rapid_title" msgid="1809795402975261331">"ปริมาณการใช้เน็ตมือถือสูง"</string>
<string name="data_usage_rapid_body" msgid="6897825788682442715">"แอปของคุณใช้อินเทอร์เน็ตมากกว่าปกติ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index c4b14f7..49318cf 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Pinapayagan ang app na baguhin ang iyong koleksyon ng larawan."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"basahin ang mga lokasyon mula sa iyong koleksyon ng media"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Pinapayagan ang app na basahin ang mga lokasyon mula sa iyong koleksyon ng media."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Walang biometric hardware"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hindi buo ang natukoy na fingerprint. Pakisubukang muli."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Hindi maproseso ang fingerprint. Pakisubukang muli."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Marumi ang sensor ng fingerprint. Pakilinis at subukang muli."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Masyadong mabagal ang paggalaw ng daliri. Pakisubukang muli."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Hindi nakilala"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Hindi nakilala"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Na-authenticate ang fingerprint"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Hindi available ang hardware na ginagamitan ng fingerprint."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Hindi maiimbak ang fingerprint. Mangyaring mag-alis ng umiiral nang fingerprint."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 67991e1..c6d18ec 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Uygulamanın fotoğraf koleksiyonunuzu değiştirmesine izin verir."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"medya koleksiyonunuzdaki konumları okuma"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Uygulamanın medya koleksiyonunuzdaki konumları okumasına izin verir."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biyometrik donanım kullanılamıyor"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Parmak izinin tümü algılanamadı. Lütfen tekrar deneyin."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Parmak izi işlenemedi. Lütfen tekrar deneyin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Parmak izi sensörü kirli. Lütfen temizleyin ve tekrar deneyin."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Parmak hareketi çok yavaştı. Lütfen tekrar deneyin."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Tanınmadı"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmadı"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Parmak izi kimlik doğrulaması yapıldı"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Parmak izi donanımı kullanılamıyor."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Parmak izi depolanamıyor. Lütfen mevcut parmak izlerinden birini kaldırın."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 45b8dd4..6e6dd9a 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -520,6 +520,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Додаток зможе змінювати вашу колекцію фотографій."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"розпізнавати геодані з колекції медіа-вмісту"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Додаток зможе розпізнавати геодані з вашої колекції медіа-вмісту."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Біометричне апаратне забезпечення недоступне"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Відбиток розпізнано частково. Повторіть спробу."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не вдалось обробити відбиток. Повторіть спробу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчик відбитків забруднився. Очистьте його та повторіть спробу."</string>
@@ -527,7 +528,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Ви провели пальцем надто повільно. Повторіть спробу."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Не розпізнано"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Не розпізнано"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Відбиток автентифіковано"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Апаратне забезпечення для сканування відбитка недоступне."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Не вдалося зберегти відбиток. Видаліть наявний відбиток."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index fffad9b..2a2ca62 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"ایپ کو آپ کی تصویر کے مجموعے میں ترمیم کی اجازت دیتا ہے۔"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"اپنی میڈيا کے مجموعے سے مقامات پڑھیں"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"ایپ کو آپ کی میڈيا کے مجموعے سے مقامات پڑھنے کی اجازت دیتا ہے۔"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"بایومیٹرک ہارڈ ویئر دستیاب نہیں ہے"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"جزوی فنگر پرنٹ کی شناخت ہوئی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"فنگر پرنٹ پر کارروائی نہیں کی جا سکی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"فنگر پرنٹ سینسر گندا ہے۔ براہ کرم صاف کریں اور دوبارہ کوشش کریں۔"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"انگلی کو بہت آہستہ ہٹایا گیا۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"تسلیم شدہ نہیں ہے"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"تسلیم شدہ نہیں ہے"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"فنگر پرنٹ کی تصدیق ہو گئی"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"فنگر پرنٹ ہارڈ ویئر دستیاب نہیں ہے۔"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"فنگر پرنٹ اسٹور نہیں کیا جا سکتا ہے۔ براہ کرم ایک موجودہ فنگر پرنٹ ہٹائیں۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 684e78a..7ffc7e7 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -284,7 +284,7 @@
<string name="permgrouprequest_sms" msgid="7168124215838204719">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun SMS xabarlarni yuborish va ko‘rishga ruxsat berilsinmi?"</string>
<string name="permgrouplab_storage" msgid="1971118770546336966">"Xotira"</string>
<string name="permgroupdesc_storage" msgid="637758554581589203">"qurilmangizdagi surat, multimedia va fayllarga kirish"</string>
- <string name="permgrouprequest_storage" msgid="7885942926944299560">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun qurilmangizdagi surat, multimedia va fayllarga ruxsat berilsinmi?"</string>
+ <string name="permgrouprequest_storage" msgid="7885942926944299560">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> ilovasiga qurilmangizdagi surat, multimedia va fayllarga kirish uchun ruxsat berilsinmi?"</string>
<string name="permgrouplab_microphone" msgid="171539900250043464">"Mikrofon"</string>
<string name="permgroupdesc_microphone" msgid="4988812113943554584">"ovoz yozib olish"</string>
<string name="permgrouprequest_microphone" msgid="9167492350681916038">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> uchun audio yozib olishga ruxsat berilsinmi?"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Ilovaga suratlar to‘plamingizni o‘zgartirishga ruxsat beradi."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"multimedia to‘plamidan joylashuv axborotini o‘qish"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ilovaga multimedia to‘plamingizdan joylashuv axborotini o‘qishga ruxsat beradi."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Biometrik sensor ishlamayapti"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmoq izi qisman aniqlandi. Qayta urinib ko‘ring."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmoq izi aniqlanmadi. Qayta urinib ko‘ring."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Barmoq izi skaneri kirlangan. Uni tozalab, keyin qayta urinib ko‘ring."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Barmoq juda sekin harakatlandi. Qayta urinib ko‘ring."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Aniqlanmadi"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Aniqlanmadi"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Barmoq izi tekshirildi"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Barmoq izi skaneri ish holatida emas."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Barmoq izini saqlab bo‘lmadi. Mavjud barmoq izlaridan birini o‘chirib tashlang."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index b106ec5..a5f458b 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"đọc vị trí từ bộ sưu tập phương tiện"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Không có phần cứng sinh trắc học"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Đã phát hiện được một phần vân tay. Vui lòng thử lại."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Không thể xử lý vân tay. Vui lòng thử lại."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Cảm biến vân tay bị bẩn. Hãy làm sạch và thử lại."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Di chuyển ngón tay quá chậm. Vui lòng thử lại."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Không nhận dạng được"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Không nhận dạng được"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Đã xác thực vân tay"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Phần cứng vân tay không khả dụng."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Không thể lưu vân tay. Vui lòng xóa vân tay hiện có."</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index a96a96d..cd809b8 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -70,4 +70,7 @@
<!-- Default Gravity setting for the system Toast view. Equivalent to: Gravity.CENTER -->
<integer name="config_toastDefaultGravity">0x00000011</integer>
+
+ <!-- Default hyphenation frequency setting (0=NONE, 1=NORMAL, 2=FULL). -->
+ <item name="config_preferredHyphenationFrequency" format="integer" type="dimen">1</item>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 5aca3ff..0b04e08 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -296,7 +296,7 @@
<string name="permgrouprequest_calllog" msgid="8487355309583773267">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问您的手机通话记录吗?"</string>
<string name="permgrouplab_phone" msgid="5229115638567440675">"电话"</string>
<string name="permgroupdesc_phone" msgid="6234224354060641055">"拨打电话和管理通话"</string>
- <string name="permgrouprequest_phone" msgid="9166979577750581037">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>拨打电话和管理通话吗?"</string>
+ <string name="permgrouprequest_phone" msgid="9166979577750581037">"允许“<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>”拨打电话和管理通话吗?"</string>
<string name="permgrouplab_sensors" msgid="416037179223226722">"身体传感器"</string>
<string name="permgroupdesc_sensors" msgid="7147968539346634043">"访问与您的生命体征相关的传感器数据"</string>
<string name="permgrouprequest_sensors" msgid="6349806962814556786">"允许<b><xliff:g id="APP_NAME">%1$s</xliff:g></b>访问与您的生命体征相关的传感器数据吗?"</string>
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"允许该应用修改您的照片收藏。"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"从您的媒体收藏中读取位置信息"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"允许该应用从您的媒体收藏中读取位置信息。"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"生物识别硬件无法使用"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"仅检测到部分指纹,请重试。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"无法处理指纹,请重试。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指纹传感器有脏污。请擦拭干净,然后重试。"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"手指移动太慢,请重试。"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"无法识别"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"无法识别"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"已验证指纹"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"指纹硬件无法使用。"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"无法存储指纹。请移除一个现有的指纹。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 79c9cc4..87eb41f 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"允許應用程式修改您的相片集。"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"讀取媒體集的位置"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"允許應用程式讀取媒體集的位置。"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"無法使用生物識別硬件"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"只偵測到部分指紋。請再試一次。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋。請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器不乾淨。請清潔後再試一次。"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"手指移動太慢,請重試。"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"未能辨別"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"未能識別"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"驗證咗指紋"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"無法使用指紋軟件。"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"指紋無法儲存。請移除現有指紋。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5143718..bd4ea2a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"允許應用程式修改你的相片收藏。"</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"讀取你的媒體收藏的位置資訊"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"允許應用程式讀取你的媒體收藏的位置資訊。"</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"找不到生物特徵辨識硬體"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"僅偵測到部分指紋,請再試一次。"</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋,請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器有髒汙。請清潔感應器,然後再試一次。"</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"手指移動速度過慢,請再試一次。"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"無法識別"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"無法辨識"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"指紋驗證成功"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"指紋硬體無法使用。"</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"無法儲存指紋,請先移除現有指紋。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index fbe0dc7..fd6614ce 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -514,6 +514,7 @@
<string name="permdesc_imagesWrite" msgid="7073662756617474375">"Ivumela uhlelo lwakho lokusebenza ukuthi lilungise iqoqo lakho lesithombe."</string>
<string name="permlab_mediaLocation" msgid="8675148183726247864">"funda izindawo kusukela kuqoqo lakho lemidiya"</string>
<string name="permdesc_mediaLocation" msgid="2237023389178865130">"Ivumela uhlelo lokusebenza ukuthi lifunde izindawo kusukela kuqoqo lakho lemidiya."</string>
+ <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"I-Biometric hardware ayitholakali"</string>
<string name="fingerprint_acquired_partial" msgid="735082772341716043">"Izigxivizo zeminwe ezincane zitholiwe. Sicela uzame futhi."</string>
<string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ayikwazanga ukucubungula izigxivizo zeminwe. Sicela uzame futhi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Inzwa yezigxivizo zeminwe ingcolile. Sicela uyihlanze uphinde uzame futhi."</string>
@@ -521,7 +522,7 @@
<string name="fingerprint_acquired_too_slow" msgid="59250885689661653">"Umnwe uhanjiswe kancane kakhulu. Sicela uzame futhi."</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
- <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Akubonwa"</string>
+ <string name="biometric_not_recognized" msgid="5770511773560736082">"Akwaziwa"</string>
<string name="fingerprint_authenticated" msgid="5309333983002526448">"Isingxivizo somunwe sigunyaziwe"</string>
<string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Izingxenyekazi zekhompuyutha zezingxivizo zeminwe azitholakali."</string>
<string name="fingerprint_error_no_space" msgid="1055819001126053318">"Izigxivizo zeminwe azikwazi ukugcinwa. Sicela ususe izigxivizo zeminwe ezikhona."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 64a9e6d..3fed8a3 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4574,6 +4574,8 @@
<attr name="letterSpacing" format="float" />
<!-- Font feature settings. -->
<attr name="fontFeatureSettings" format="string" />
+ <!-- Font variation settings. -->
+ <attr name="fontVariationSettings" format="string"/>
</declare-styleable>
<declare-styleable name="TextClock">
<!-- Specifies the formatting pattern used to show the time and/or date
@@ -4922,6 +4924,8 @@
<attr name="letterSpacing" />
<!-- Font feature settings. -->
<attr name="fontFeatureSettings" />
+ <!-- Font variation settings. -->
+ <attr name="fontVariationSettings" />
<!-- Break strategy (control over paragraph layout). -->
<attr name="breakStrategy">
<!-- Line breaking uses simple strategy. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c8ad31f..293d90e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -913,7 +913,7 @@
<!-- Control whether to lock day/night mode change from normal application. When it is
true, day / night mode change is only allowed to apps with MODIFY_DAY_NIGHT_MODE
permission. -->
- <bool name="config_lockDayNightMode">false</bool>
+ <bool name="config_lockDayNightMode">true</bool>
<!-- Control the default night mode to use when there is no other mode override set.
One of the following values (see UiModeManager.java):
@@ -2116,6 +2116,9 @@
<!-- Type of the reach sensor. Empty if reach is not supported. -->
<string name="config_dozeReachSensorType" translatable="false"></string>
+ <!-- Type of the wake up sensor. Empty if not supported. -->
+ <string name="config_dozeWakeScreenSensorType" translatable="false"></string>
+
<!-- Control whether the always on display mode is available. This should only be enabled on
devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND
states. -->
@@ -2793,7 +2796,7 @@
<item>SUPL_PORT=7275</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
- <item>SUPL_ES=0</item>
+ <item>SUPL_ES=1</item>
<item>LPP_PROFILE=0</item>
<item>USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=1</item>
<item>A_GLONASS_POS_PROTOCOL_SELECT=0</item>
@@ -3352,9 +3355,6 @@
to occur. The expand button will have increased touch boundaries to accomodate this. -->
<bool name="config_notificationHeaderClickableForExpand">false</bool>
- <!-- Configuration for automotive -->
- <bool name="enable_pbap_pce_profile">false</bool>
-
<!-- Default data warning level in mb -->
<integer name="default_data_warning_level_mb">2048</integer>
@@ -3510,6 +3510,9 @@
<item>"wifi"</item>
</string-array>
+ <!-- Default hyphenation frequency setting (0=NONE, 1=NORMAL, 2=FULL). -->
+ <item name="config_preferredHyphenationFrequency" format="integer" type="dimen">0</item>
+
<!-- Package name for ManagedProvisioning which is responsible for provisioning work profiles. -->
<string name="config_managed_provisioning_package" translatable="false">com.android.managedprovisioning</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d733207..365e4a4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2881,55 +2881,55 @@
<!-- Title for EditText context menu [CHAR LIMIT=20] -->
<string name="editTextMenuTitle">Text actions</string>
- <!-- Label for item in the text selection menu to trigger an Email app [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger an Email app. Should be a verb. [CHAR LIMIT=20] -->
<string name="email">Email</string>
<!-- Accessibility description for an item in the text selection menu to trigger an Email app [CHAR LIMIT=NONE] -->
<string name="email_desc">Email selected address</string>
- <!-- Label for item in the text selection menu to trigger a Dialer app [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger a Dialer app. Should be a verb. [CHAR LIMIT=20] -->
<string name="dial">Call</string>
<!-- Accessibility description for an item in the text selection menu to call a phone number [CHAR LIMIT=NONE] -->
<string name="dial_desc">Call selected phone number</string>
- <!-- Label for item in the text selection menu to trigger a Map app [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger a Map app. Should be a verb. [CHAR LIMIT=20] -->
<string name="map">Map</string>
<!-- Accessibility description for an item in the text selection menu to open maps for an address [CHAR LIMIT=NONE] -->
<string name="map_desc">Locate selected address</string>
- <!-- Label for item in the text selection menu to trigger a Browser app [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger a Browser app. Should be a verb. [CHAR LIMIT=20] -->
<string name="browse">Open</string>
<!-- Accessibility description for an item in the text selection menu to open a URL in a browser [CHAR LIMIT=NONE] -->
<string name="browse_desc">Open selected URL</string>
- <!-- Label for item in the text selection menu to trigger an SMS app [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger an SMS app. Should be a verb. [CHAR LIMIT=20] -->
<string name="sms">Message</string>
<!-- Accessibility description for an item in the text selection menu to send an SMS to a phone number [CHAR LIMIT=NONE] -->
<string name="sms_desc">Message selected phone number</string>
- <!-- Label for item in the text selection menu to trigger adding a contact [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to trigger adding a contact. Should be a verb. [CHAR LIMIT=20] -->
<string name="add_contact">Add</string>
<!-- Accessibility description for an item in the text selection menu to add the selected detail to contacts [CHAR LIMIT=NONE] -->
<string name="add_contact_desc">Add to contacts</string>
- <!-- Label for item in the text selection menu to view the calendar for the selected time/date [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to view the calendar for the selected time/date. Should be a verb. [CHAR LIMIT=20] -->
<string name="view_calendar">View</string>
<!-- Accessibility description for an item in the text selection menu to view the calendar for a date [CHAR LIMIT=NONE]-->
<string name="view_calendar_desc">View selected time in calendar</string>
- <!-- Label for item in the text selection menu to create a calendar event at the selected time/date [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to create a calendar event at the selected time/date. Should be a verb. [CHAR LIMIT=20] -->
<string name="add_calendar_event">Schedule</string>
<!-- Accessibility description for an item in the text selection menu to schedule an event for a date [CHAR LIMIT=NONE] -->
<string name="add_calendar_event_desc">Schedule event for selected time</string>
- <!-- Label for item in the text selection menu to track a selected flight number [CHAR LIMIT=20] -->
+ <!-- Label for item in the text selection menu to track a selected flight number. Should be a verb. [CHAR LIMIT=20] -->
<string name="view_flight">Track</string>
<!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index fafcf93..ef286e2 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -506,13 +506,13 @@
<item name="textEditSuggestionHighlightStyle">?attr/textEditSuggestionHighlightStyle</item>
<item name="textCursorDrawable">?attr/textCursorDrawable</item>
<item name="breakStrategy">high_quality</item>
- <item name="hyphenationFrequency">none</item>
+ <item name="hyphenationFrequency">@dimen/config_preferredHyphenationFrequency</item>
</style>
<style name="Widget.CheckedTextView">
<item name="textAlignment">viewStart</item>
<item name="breakStrategy">high_quality</item>
- <item name="hyphenationFrequency">none</item>
+ <item name="hyphenationFrequency">@dimen/config_preferredHyphenationFrequency</item>
</style>
<style name="Widget.TextView.ListSeparator">
@@ -540,7 +540,7 @@
<item name="textColor">?attr/editTextColor</item>
<item name="gravity">center_vertical</item>
<item name="breakStrategy">simple</item>
- <item name="hyphenationFrequency">none</item>
+ <item name="hyphenationFrequency">@dimen/config_preferredHyphenationFrequency</item>
<item name="defaultFocusHighlightEnabled">false</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e209985..92cca72 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3259,8 +3259,6 @@
<java-symbol type="style" name="Theme.DeviceDefault.QuickSettings" />
- <java-symbol type="bool" name="enable_pbap_pce_profile" />
-
<java-symbol type="integer" name="default_data_warning_level_mb" />
<java-symbol type="bool" name="config_useVideoPauseWorkaround" />
<java-symbol type="bool" name="config_sendPackageName" />
@@ -3464,4 +3462,5 @@
<java-symbol type="integer" name="db_wal_truncate_size" />
<java-symbol type="integer" name="config_wakeUpDelayDoze" />
+ <java-symbol type="string" name="config_dozeWakeScreenSensorType" />
</resources>
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index dd34f1f..1c4039b 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.support.test.InstrumentationRegistry;
@@ -43,6 +44,7 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
+import java.util.HashSet;
import java.util.Locale;
@SmallTest
@@ -110,13 +112,14 @@
private static void buildSystemFallback(String xml,
ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
+ final HashSet<Font> availableFonts = new HashSet<>();
try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) {
fos.write(xml.getBytes(Charset.forName("UTF-8")));
} catch (IOException e) {
throw new RuntimeException(e);
}
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML,
- TEST_FONT_DIR, fallbackMap);
+ TEST_FONT_DIR, fallbackMap, availableFonts);
Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases);
}
@@ -124,9 +127,10 @@
public void testBuildSystemFallback() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final HashSet<Font> availableFonts = new HashSet<>();
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML,
- SYSTEM_FONT_DIR, fallbackMap);
+ SYSTEM_FONT_DIR, fallbackMap, availableFonts);
assertNotNull(aliases);
assertFalse(fallbackMap.isEmpty());
@@ -487,7 +491,7 @@
+ " <family lang='de'>"
+ " <font weight='400' style='normal'>a3em.ttf</font>"
+ " </family>"
- + " <family lang='it fr'>"
+ + " <family lang='it,fr'>"
+ " <font weight='400' style='normal'>b3em.ttf</font>"
+ " </family>"
+ "</familyset>";
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 189a4aa..e84aed1 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -192,6 +192,7 @@
Settings.Global.DEFAULT_DNS_SERVER,
Settings.Global.DEFAULT_INSTALL_LOCATION,
Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
+ Settings.Global.DEFAULT_USER_ID_TO_BOOT_INTO,
Settings.Global.DESK_DOCK_SOUND,
Settings.Global.DESK_UNDOCK_SOUND,
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
@@ -273,6 +274,7 @@
Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
Settings.Global.LANG_ID_UPDATE_METADATA_URL,
+ Settings.Global.LAST_ACTIVE_USER_ID,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
@@ -280,6 +282,7 @@
Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
Settings.Global.LOCK_SOUND,
+ Settings.Global.LOOPER_STATS,
Settings.Global.LOW_BATTERY_SOUND,
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT,
Settings.Global.LOW_POWER_MODE,
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index 6c34043..355be61 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Typeface;
+import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.SystemFonts;
import android.support.test.InstrumentationRegistry;
@@ -32,6 +33,7 @@
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
+import java.util.HashSet;
public class FontFallbackSetup implements AutoCloseable {
private final String[] mTestFontFiles;
@@ -74,8 +76,9 @@
}
final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+ final HashSet<Font> availableFonts = new HashSet<>();
final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml,
- mTestFontsDir, fallbackMap);
+ mTestFontsDir, fallbackMap, availableFonts);
Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases);
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 3c04895..3e03414 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -40,7 +40,7 @@
// and assertAccessibilityEventCleared
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 32;
+ private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 33;
// The number of fields tested in the corresponding CTS AccessibilityRecordTest:
// assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord,
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 7fa6ce4..922b79a 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -57,7 +57,7 @@
// The number of flags held in boolean properties. Their values should also be double-checked
// in the methods above.
- private static final int NUM_BOOLEAN_PROPERTIES = 22;
+ private static final int NUM_BOOLEAN_PROPERTIES = 17;
@Test
public void testStandardActions_serializationFlagIsValid() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index ace6b2d..364dcfd 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -18,10 +18,7 @@
import static org.junit.Assert.assertEquals;
-import android.content.Intent;
-import android.os.BatteryManager;
import android.os.Binder;
-import android.os.OsProtoEnums;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -34,7 +31,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -50,6 +46,7 @@
private static final int TEST_UID = 1;
private static final int REQUEST_SIZE = 2;
private static final int REPLY_SIZE = 3;
+ private final CachedDeviceState mDeviceState = new CachedDeviceState(false, true);
@Test
public void testDetailedOff() {
@@ -388,43 +385,27 @@
}
@Test
- public void testDataResetWhenInitialStateSet() {
+ public void testNoDataCollectedBeforeInitialDeviceStateSet() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
+ bcs.setDeviceState(null);
bcs.setDetailedTracking(true);
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.time += 10;
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
- bcs.setInitialState(true, true);
+ bcs.setDeviceState(mDeviceState.getReadonlyClient());
SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
assertEquals(0, uidEntries.size());
}
@Test
- public void testScreenAndChargerInitialStates() {
- TestBinderCallsStats bcs = new TestBinderCallsStats();
- bcs.setDetailedTracking(true);
- Binder binder = new Binder();
- bcs.setInitialState(true /** screen iteractive */, false);
-
- CallSession callSession = bcs.callStarted(binder, 1);
- bcs.time += 10;
- bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
-
- List<BinderCallsStats.CallStat> callStatsList =
- new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
- assertEquals(true, callStatsList.get(0).screenInteractive);
- }
-
- @Test
public void testNoDataCollectedOnCharger() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
- .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
- bcs.getBroadcastReceiver().onReceive(null, intent);
+ mDeviceState.setCharging(true);
+
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -436,7 +417,7 @@
public void testScreenOff() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_OFF));
+ mDeviceState.setScreenInteractive(false);
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -453,7 +434,7 @@
public void testScreenOn() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+ mDeviceState.setScreenInteractive(true);
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -470,9 +451,8 @@
public void testOnCharger() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
- .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
- bcs.getBroadcastReceiver().onReceive(null, intent);
+ mDeviceState.setCharging(true);
+
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -484,9 +464,8 @@
public void testOnBattery() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
- .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
- bcs.getBroadcastReceiver().onReceive(null, intent);
+ mDeviceState.setCharging(false);
+
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
@@ -522,7 +501,6 @@
public void testGetExportedStatsWhenDetailedTrackingEnabled() {
TestBinderCallsStats bcs = new TestBinderCallsStats();
bcs.setDetailedTracking(true);
- bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
Binder binder = new Binder();
CallSession callSession = bcs.callStarted(binder, 1);
@@ -561,7 +539,7 @@
assertEquals(0, bcs.getExceptionCounts().size());
}
- static class TestBinderCallsStats extends BinderCallsStats {
+ class TestBinderCallsStats extends BinderCallsStats {
int callingUid = TEST_UID;
long time = 1234;
long elapsedTime = 0;
@@ -580,6 +558,7 @@
}
});
setSamplingInterval(1);
+ setDeviceState(mDeviceState.getReadonlyClient());
}
@Override
diff --git a/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
new file mode 100644
index 0000000..0eb3d06
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/LooperStatsTest.java
@@ -0,0 +1,402 @@
+/*
+ * 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.internal.os;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Comparator;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public final class LooperStatsTest {
+ private HandlerThread mThreadFirst;
+ private HandlerThread mThreadSecond;
+ private Handler mHandlerFirst;
+ private Handler mHandlerSecond;
+ private Handler mHandlerAnonymous;
+ private CachedDeviceState mDeviceState;
+
+ @Before
+ public void setUp() {
+ // The tests are all single-threaded. HandlerThreads are created to allow creating Handlers
+ // and to test Thread name collection.
+ mThreadFirst = new HandlerThread("TestThread1");
+ mThreadSecond = new HandlerThread("TestThread2");
+ mThreadFirst.start();
+ mThreadSecond.start();
+
+ mHandlerFirst = new TestHandlerFirst(mThreadFirst.getLooper());
+ mHandlerSecond = new TestHandlerSecond(mThreadSecond.getLooper());
+ mHandlerAnonymous = new Handler(mThreadFirst.getLooper()) {
+ /* To create an anonymous subclass. */
+ };
+ mDeviceState = new CachedDeviceState();
+ mDeviceState.setCharging(false);
+ mDeviceState.setScreenInteractive(true);
+ }
+
+ @After
+ public void tearDown() {
+ mThreadFirst.quit();
+ mThreadSecond.quit();
+ }
+
+ @Test
+ public void testSingleMessageDispatched() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+ Object token = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token, mHandlerFirst.obtainMessage(1000));
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(1);
+ LooperStats.ExportedEntry entry = entries.get(0);
+ assertThat(entry.threadName).isEqualTo("TestThread1");
+ assertThat(entry.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+ assertThat(entry.messageName).isEqualTo("0x3e8" /* 1000 in hex */);
+ assertThat(entry.isInteractive).isEqualTo(true);
+ assertThat(entry.messageCount).isEqualTo(1);
+ assertThat(entry.recordedMessageCount).isEqualTo(1);
+ assertThat(entry.exceptionCount).isEqualTo(0);
+ assertThat(entry.totalLatencyMicros).isEqualTo(100);
+ assertThat(entry.maxLatencyMicros).isEqualTo(100);
+ assertThat(entry.cpuUsageMicros).isEqualTo(10);
+ assertThat(entry.maxCpuUsageMicros).isEqualTo(10);
+ }
+
+ @Test
+ public void testThrewException() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+ Object token = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(10);
+ looperStats.dispatchingThrewException(token, mHandlerFirst.obtainMessage(7),
+ new ArithmeticException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(1);
+ LooperStats.ExportedEntry entry = entries.get(0);
+ assertThat(entry.threadName).isEqualTo("TestThread1");
+ assertThat(entry.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+ assertThat(entry.messageName).isEqualTo("0x7" /* 7 in hex */);
+ assertThat(entry.isInteractive).isEqualTo(true);
+ assertThat(entry.messageCount).isEqualTo(0);
+ assertThat(entry.recordedMessageCount).isEqualTo(0);
+ assertThat(entry.exceptionCount).isEqualTo(1);
+ assertThat(entry.totalLatencyMicros).isEqualTo(0);
+ assertThat(entry.maxLatencyMicros).isEqualTo(0);
+ assertThat(entry.cpuUsageMicros).isEqualTo(0);
+ assertThat(entry.maxCpuUsageMicros).isEqualTo(0);
+ }
+
+ @Test
+ public void testMultipleMessagesDispatched() {
+ TestableLooperStats looperStats = new TestableLooperStats(2, 100);
+
+ // Contributes to entry2.
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+ // Contributes to entry2.
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(50);
+ looperStats.tickThreadTime(20);
+ looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1000));
+
+ // Contributes to entry3.
+ Object token3 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(10);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token3, mHandlerSecond.obtainMessage().setCallback(() -> {
+ }));
+
+ // Contributes to entry1.
+ Object token4 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(100);
+ looperStats.messageDispatched(token4, mHandlerAnonymous.obtainMessage(1));
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(3);
+ entries.sort(Comparator.comparing(e -> e.handlerClassName));
+
+ // Captures data for token4 call.
+ LooperStats.ExportedEntry entry1 = entries.get(0);
+ assertThat(entry1.threadName).isEqualTo("TestThread1");
+ assertThat(entry1.handlerClassName).isEqualTo("com.android.internal.os.LooperStatsTest$1");
+ assertThat(entry1.messageName).isEqualTo("0x1" /* 1 in hex */);
+ assertThat(entry1.messageCount).isEqualTo(1);
+ assertThat(entry1.recordedMessageCount).isEqualTo(0);
+ assertThat(entry1.exceptionCount).isEqualTo(0);
+ assertThat(entry1.totalLatencyMicros).isEqualTo(0);
+ assertThat(entry1.maxLatencyMicros).isEqualTo(0);
+ assertThat(entry1.cpuUsageMicros).isEqualTo(0);
+ assertThat(entry1.maxCpuUsageMicros).isEqualTo(0);
+
+ // Captures data for token1 and token2 calls.
+ LooperStats.ExportedEntry entry2 = entries.get(1);
+ assertThat(entry2.threadName).isEqualTo("TestThread1");
+ assertThat(entry2.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+ assertThat(entry2.messageName).isEqualTo("0x3e8" /* 1000 in hex */);
+ assertThat(entry2.messageCount).isEqualTo(2);
+ assertThat(entry2.recordedMessageCount).isEqualTo(1);
+ assertThat(entry2.exceptionCount).isEqualTo(0);
+ assertThat(entry2.totalLatencyMicros).isEqualTo(100);
+ assertThat(entry2.maxLatencyMicros).isEqualTo(100);
+ assertThat(entry2.cpuUsageMicros).isEqualTo(10);
+ assertThat(entry2.maxCpuUsageMicros).isEqualTo(10);
+
+ // Captures data for token3 call.
+ LooperStats.ExportedEntry entry3 = entries.get(2);
+ assertThat(entry3.threadName).isEqualTo("TestThread2");
+ assertThat(entry3.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerSecond");
+ assertThat(entry3.messageName).startsWith(
+ "com.android.internal.os.-$$Lambda$LooperStatsTest$");
+ assertThat(entry3.messageCount).isEqualTo(1);
+ assertThat(entry3.recordedMessageCount).isEqualTo(1);
+ assertThat(entry3.exceptionCount).isEqualTo(0);
+ assertThat(entry3.totalLatencyMicros).isEqualTo(10);
+ assertThat(entry3.maxLatencyMicros).isEqualTo(10);
+ assertThat(entry3.cpuUsageMicros).isEqualTo(10);
+ assertThat(entry3.maxCpuUsageMicros).isEqualTo(10);
+ }
+
+ @Test
+ public void testDataNotCollectedBeforeDeviceStateSet() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ looperStats.setDeviceState(null);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(0);
+ }
+
+ @Test
+ public void testDataNotCollectedOnCharger() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ mDeviceState.setCharging(true);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(0);
+ }
+
+ @Test
+ public void testScreenStateCollected() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+
+ mDeviceState.setScreenInteractive(true);
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token2, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ Object token3 = looperStats.messageDispatchStarting();
+ // If screen state changed during the call, we take the final state into account.
+ mDeviceState.setScreenInteractive(false);
+ looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1000));
+ Object token4 = looperStats.messageDispatchStarting();
+ looperStats.dispatchingThrewException(token4, mHandlerFirst.obtainMessage(1000),
+ new IllegalArgumentException());
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(2);
+ entries.sort(Comparator.comparing(e -> e.isInteractive));
+
+ LooperStats.ExportedEntry entry1 = entries.get(0);
+ assertThat(entry1.isInteractive).isEqualTo(false);
+ assertThat(entry1.messageCount).isEqualTo(1);
+ assertThat(entry1.exceptionCount).isEqualTo(1);
+
+ LooperStats.ExportedEntry entry2 = entries.get(1);
+ assertThat(entry2.isInteractive).isEqualTo(true);
+ assertThat(entry2.messageCount).isEqualTo(1);
+ assertThat(entry2.exceptionCount).isEqualTo(1);
+ }
+
+ @Test
+ public void testMessagesOverSizeCap() {
+ TestableLooperStats looperStats = new TestableLooperStats(2, 1 /* sizeCap */);
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(100);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(50);
+ looperStats.tickThreadTime(20);
+ looperStats.messageDispatched(token2, mHandlerFirst.obtainMessage(1001));
+
+ Object token3 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(10);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token3, mHandlerFirst.obtainMessage(1002));
+
+ Object token4 = looperStats.messageDispatchStarting();
+ looperStats.tickRealtime(10);
+ looperStats.tickThreadTime(10);
+ looperStats.messageDispatched(token4, mHandlerSecond.obtainMessage(1003));
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ assertThat(entries).hasSize(2);
+ entries.sort(Comparator.comparing(e -> e.handlerClassName));
+
+ LooperStats.ExportedEntry entry1 = entries.get(0);
+ assertThat(entry1.threadName).isEqualTo("");
+ assertThat(entry1.handlerClassName).isEqualTo("");
+ assertThat(entry1.messageName).isEqualTo("OVERFLOW");
+ assertThat(entry1.messageCount).isEqualTo(3);
+ assertThat(entry1.recordedMessageCount).isEqualTo(1);
+ assertThat(entry1.exceptionCount).isEqualTo(0);
+ assertThat(entry1.totalLatencyMicros).isEqualTo(10);
+ assertThat(entry1.maxLatencyMicros).isEqualTo(10);
+ assertThat(entry1.cpuUsageMicros).isEqualTo(10);
+ assertThat(entry1.maxCpuUsageMicros).isEqualTo(10);
+
+ LooperStats.ExportedEntry entry2 = entries.get(1);
+ assertThat(entry2.threadName).isEqualTo("TestThread1");
+ assertThat(entry2.handlerClassName).isEqualTo(
+ "com.android.internal.os.LooperStatsTest$TestHandlerFirst");
+ }
+
+ @Test
+ public void testInvalidTokensCauseException() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ assertThrows(ClassCastException.class,
+ () -> looperStats.dispatchingThrewException(new Object(),
+ mHandlerFirst.obtainMessage(),
+ new ArithmeticException()));
+ assertThrows(ClassCastException.class,
+ () -> looperStats.messageDispatched(new Object(), mHandlerFirst.obtainMessage()));
+ assertThrows(ClassCastException.class,
+ () -> looperStats.messageDispatched(123, mHandlerFirst.obtainMessage()));
+ assertThrows(ClassCastException.class,
+ () -> looperStats.messageDispatched(mHandlerFirst.obtainMessage(),
+ mHandlerFirst.obtainMessage()));
+
+ assertThat(looperStats.getEntries()).hasSize(0);
+ }
+
+ @Test
+ public void testTracksMultipleHandlerInstancesIfSameClass() {
+ TestableLooperStats looperStats = new TestableLooperStats(1, 100);
+ Handler handlerFirstAnother = new TestHandlerFirst(mHandlerFirst.getLooper());
+
+ Object token1 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token1, mHandlerFirst.obtainMessage(1000));
+
+ Object token2 = looperStats.messageDispatchStarting();
+ looperStats.messageDispatched(token2, handlerFirstAnother.obtainMessage(1000));
+
+ assertThat(looperStats.getEntries()).hasSize(1);
+ assertThat(looperStats.getEntries().get(0).messageCount).isEqualTo(2);
+ }
+
+ private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
+ try {
+ r.run();
+ Assert.fail("Expected " + exceptionClass + " to be thrown.");
+ } catch (Exception exception) {
+ assertThat(exception).isInstanceOf(exceptionClass);
+ }
+ }
+
+ private final class TestableLooperStats extends LooperStats {
+ private static final long INITIAL_MICROS = 10001000123L;
+ private int mCount;
+ private long mRealtimeMicros;
+ private long mThreadTimeMicros;
+ private int mSamplingInterval;
+
+ TestableLooperStats(int samplingInterval, int sizeCap) {
+ super(samplingInterval, sizeCap);
+ this.mSamplingInterval = samplingInterval;
+ this.setDeviceState(mDeviceState.getReadonlyClient());
+ }
+
+ void tickRealtime(long micros) {
+ mRealtimeMicros += micros;
+ }
+
+ void tickThreadTime(long micros) {
+ mThreadTimeMicros += micros;
+ }
+
+ @Override
+ protected long getElapsedRealtimeMicro() {
+ return INITIAL_MICROS + mRealtimeMicros;
+ }
+
+ @Override
+ protected long getThreadTimeMicro() {
+ return INITIAL_MICROS + mThreadTimeMicros;
+ }
+
+ @Override
+ protected boolean shouldCollectDetailedData() {
+ return mCount++ % mSamplingInterval == 0;
+ }
+ }
+
+ private static final class TestHandlerFirst extends Handler {
+ TestHandlerFirst(Looper looper) {
+ super(looper);
+ }
+ }
+
+ private static final class TestHandlerSecond extends Handler {
+ TestHandlerSecond(Looper looper) {
+ super(looper);
+ }
+ }
+}
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index be9347a..c84c035 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -137,7 +137,7 @@
<font weight="400" style="normal" fallbackFor="serif">NotoSerifArmenian-Regular.otf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifArmenian-Bold.otf</font>
</family>
- <family lang="und-Geor und-Geok">
+ <family lang="und-Geor,und-Geok">
<font weight="400" style="normal">NotoSansGeorgian-Regular.otf</font>
<font weight="500" style="normal">NotoSansGeorgian-Medium.otf</font>
<font weight="700" style="normal">NotoSansGeorgian-Bold.otf</font>
@@ -538,7 +538,7 @@
<font weight="400" style="normal" index="2">NotoSansCJK-Regular.ttc</font>
<font weight="400" style="normal" index="2" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
- <family lang="zh-Hant zh-Bopo">
+ <family lang="zh-Hant,zh-Bopo">
<font weight="400" style="normal" index="3">NotoSansCJK-Regular.ttc</font>
<font weight="400" style="normal" index="3" fallbackFor="serif">NotoSerifCJK-Regular.ttc</font>
</family>
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index e3e8380..82435d5 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -74,8 +74,7 @@
private static FontConfig.Family readFamily(XmlPullParser parser)
throws XmlPullParserException, IOException {
final String name = parser.getAttributeValue(null, "name");
- final String lang = parser.getAttributeValue(null, "lang");
- final String[] langs = lang == null ? null : lang.split("\\s+");
+ final String lang = parser.getAttributeValue("", "lang");
final String variant = parser.getAttributeValue(null, "variant");
final List<FontConfig.Font> fonts = new ArrayList<FontConfig.Font>();
while (parser.next() != XmlPullParser.END_TAG) {
@@ -95,7 +94,7 @@
intVariant = FontConfig.Family.VARIANT_ELEGANT;
}
}
- return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), langs,
+ return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang,
intVariant);
}
@@ -126,8 +125,8 @@
}
}
String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
- return new FontConfig.Font(sanitizedName, index,
- axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
+ return new FontConfig.Font(sanitizedName, index, axes.toArray(
+ new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
}
private static FontVariationAxis readAxis(XmlPullParser parser)
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index 5a78530..c3449dd 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -16,6 +16,9 @@
package android.graphics;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
/**
* An Insets instance holds four integer offsets which describe changes to the four
* edges of a Rectangle. By convention, positive values move edges towards the
@@ -24,7 +27,7 @@
* Insets are immutable so may be treated as values.
*
*/
-public class Insets {
+public final class Insets {
public static final Insets NONE = new Insets(0, 0, 0, 0);
public final int left;
@@ -51,7 +54,7 @@
*
* @return Insets instance with the appropriate values
*/
- public static Insets of(int left, int top, int right, int bottom) {
+ public static @NonNull Insets of(int left, int top, int right, int bottom) {
if (left == 0 && top == 0 && right == 0 && bottom == 0) {
return NONE;
}
@@ -65,7 +68,7 @@
*
* @return an Insets instance with the appropriate values
*/
- public static Insets of(Rect r) {
+ public static @NonNull Insets of(@Nullable Rect r) {
return (r == null) ? NONE : of(r.left, r.top, r.right, r.bottom);
}
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 9dab536..6f30653 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -18,6 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
+import android.annotation.Px;
import android.annotation.Size;
import android.annotation.UnsupportedAppUsage;
import android.graphics.fonts.FontVariationAxis;
@@ -805,25 +806,39 @@
* Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
*
* @return true if the underlineText bit is set in the paint's flags.
+ * @see #getUnderlinePosition()
+ * @see #getUnderlineThickness()
+ * @see #setUnderlineText(boolean)
*/
public final boolean isUnderlineText() {
return (getFlags() & UNDERLINE_TEXT_FLAG) != 0;
}
/**
- * Distance from top of the underline to the baseline. Positive values mean below the baseline.
- * This method returns where the underline should be drawn independent of if the underlineText
- * bit is set at the moment.
- * @hide
+ * Returns the distance from top of the underline to the baseline in pixels.
+ *
+ * The result is positive for positions that are below the baseline.
+ * This method returns where the underline should be drawn independent of if the {@link
+ * #UNDERLINE_TEXT_FLAG} bit is set.
+ *
+ * @return the position of the underline in pixels
+ * @see #isUnderlineText()
+ * @see #getUnderlineThickness()
+ * @see #setUnderlineText(boolean)
*/
- public float getUnderlinePosition() {
+ public @Px float getUnderlinePosition() {
return nGetUnderlinePosition(mNativePaint);
}
/**
- * @hide
+ * Returns the thickness of the underline in pixels.
+ *
+ * @return the thickness of the underline in pixels
+ * @see #isUnderlineText()
+ * @see #getUnderlinePosition()
+ * @see #setUnderlineText(boolean)
*/
- public float getUnderlineThickness() {
+ public @Px float getUnderlineThickness() {
return nGetUnderlineThickness(mNativePaint);
}
@@ -832,6 +847,9 @@
*
* @param underlineText true to set the underlineText bit in the paint's
* flags, false to clear it.
+ * @see #isUnderlineText()
+ * @see #getUnderlinePosition()
+ * @see #getUnderlineThickness()
*/
public void setUnderlineText(boolean underlineText) {
nSetUnderlineText(mNativePaint, underlineText);
@@ -840,26 +858,40 @@
/**
* Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
*
- * @return true if the strikeThruText bit is set in the paint's flags.
+ * @return true if the {@link #STRIKE_THRU_TEXT_FLAG} bit is set in the paint's flags.
+ * @see #getStrikeThruPosition()
+ * @see #getStrikeThruThickness()
+ * @see #setStrikeThruText(boolean)
*/
public final boolean isStrikeThruText() {
return (getFlags() & STRIKE_THRU_TEXT_FLAG) != 0;
}
/**
- * Distance from top of the strike-through line to the baseline. Negative values mean above the
- * baseline. This method returns where the strike-through line should be drawn independent of if
- * the strikeThruText bit is set at the moment.
- * @hide
+ * Distance from top of the strike-through line to the baseline in pixels.
+ *
+ * The result is negative for positions that are above the baseline.
+ * This method returns where the strike-through line should be drawn independent of if the
+ * {@link #STRIKE_THRU_TEXT_FLAG} bit is set.
+ *
+ * @return the position of the strike-through line in pixels
+ * @see #getStrikeThruThickness()
+ * @see #setStrikeThruText(boolean)
+ * @see #isStrikeThruText()
*/
- public float getStrikeThruPosition() {
+ public @Px float getStrikeThruPosition() {
return nGetStrikeThruPosition(mNativePaint);
}
/**
- * @hide
+ * Returns the thickness of the strike-through line in pixels.
+ *
+ * @return the position of the strike-through line in pixels
+ * @see #getStrikeThruPosition()
+ * @see #setStrikeThruText(boolean)
+ * @see #isStrikeThruText()
*/
- public float getStrikeThruThickness() {
+ public @Px float getStrikeThruThickness() {
return nGetStrikeThruThickness(mNativePaint);
}
@@ -868,6 +900,9 @@
*
* @param strikeThruText true to set the strikeThruText bit in the paint's
* flags, false to clear it.
+ * @see #getStrikeThruPosition()
+ * @see #getStrikeThruThickness()
+ * @see #isStrikeThruText()
*/
public void setStrikeThruText(boolean strikeThruText) {
nSetStrikeThruText(mNativePaint, strikeThruText);
@@ -1560,22 +1595,25 @@
}
/**
- * Return the paint's word-spacing for text. The default value is 0.
+ * Return the paint's extra word-spacing for text.
*
- * @return the paint's word-spacing for drawing text.
- * @hide
+ * The default value is 0.
+ *
+ * @return the paint's extra word-spacing for drawing text in pixels.
+ * @see #setWordSpacing(float)
*/
public float getWordSpacing() {
return nGetWordSpacing(mNativePaint);
}
/**
- * Set the paint's word-spacing for text. The default value is 0.
- * The value is in pixels (note the units are not the same as for
- * letter-spacing).
+ * Set the paint's extra word-spacing for text.
*
- * @param wordSpacing set the paint's word-spacing for drawing text.
- * @hide
+ * Increases the white space width between words with the given amount of pixels.
+ * The default value is 0.
+ *
+ * @param wordSpacing set the paint's extra word-spacing for drawing text in pixels.
+ * @see #getWordSpacing()
*/
public void setWordSpacing(float wordSpacing) {
nSetWordSpacing(mNativePaint, wordSpacing);
@@ -1704,26 +1742,52 @@
}
/**
- * Get the current value of hyphen edit.
+ * Get the current value of packed hyphen edit.
+ *
+ * You can extract start hyphen edit and end hyphen edit by using
+ * {@link android.text.Hyphenator#unpackStartHyphenEdit(int)} and
+ * {@link android.text.Hyphenator#unpackEndHyphenEdit(int)}.
+ *
+ * The default value is 0 which is equivalent to packed value of
+ * {@link android.text.Hyphenator#START_HYPHEN_EDIT_NO_EDIT} and
+ * {@link android.text.Hyphenator#END_HYPHEN_EDIT_NO_EDIT}.
*
* @return the current hyphen edit value
- *
- * @hide
+ * @see #setHyphenEdit(int)
*/
public int getHyphenEdit() {
return nGetHyphenEdit(mNativePaint);
}
/**
- * Set a hyphen edit on the paint (causes a hyphen to be added to text when
- * measured or drawn).
+ * Set a packed hyphen edit on the paint.
*
- * @param hyphen 0 for no edit, 1 for adding a hyphen at the end, etc.
- * Definition of various values are in the HyphenEdit class in Minikin's Hyphenator.h.
+ * By setting hyphen edit, the measurement and drawing is performed with modifying hyphenation
+ * at the start of line and end of line. For example, by passing
+ * {@link android.text.Hyphenator#END_HYPHEN_EDIT_INSERT_HYPHEN} like as follows, HYPHEN(U+2010)
+ * character is appended at the end of line.
*
- * @hide
+ * <pre>
+ * <code>
+ * Paint paint = new Paint();
+ * paint.setHyphenEdit(Hyphenator.packHyphenEdit(
+ * Hyphenator.START_HYPHEN_EDIT_NO_EDIT,
+ * Hyphenator.END_HYPHEN_EDIT_INSERT_HYPHEN));
+ * paint.measureText("abc", 0, 3); // Returns the width of "abc‐"
+ * Canvas.drawText("abc", 0, 3, 0f, 0f, paint); // Draws "abc‐"
+ * </code>
+ * </pre>
+ *
+ * You can pack start hyphen edit and end hyphen edit by
+ * {@link android.text.Hyphenator#packHyphenEdit(int,int)}
+ *
+ * The default value is 0 which is equivalent to packed value of
+ * {@link android.text.Hyphenator#START_HYPHEN_EDIT_NO_EDIT} and
+ * {@link android.text.Hyphenator#END_HYPHEN_EDIT_NO_EDIT}.
+ *
+ * @param hyphen a packed hyphen edit value.
+ * @see #getHyphenEdit()
*/
- @UnsupportedAppUsage
public void setHyphenEdit(int hyphen) {
nSetHyphenEdit(mNativePaint, hyphen);
}
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 5280642..5aa09ce 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -148,13 +148,7 @@
@UnsupportedAppUsage
private @Style int mStyle = 0;
- /**
- * A maximum value for the weight value.
- * @hide
- */
- public static final int MAX_WEIGHT = 1000;
-
- private @IntRange(from = 0, to = MAX_WEIGHT) int mWeight = 0;
+ private @IntRange(from = 0, to = android.graphics.fonts.Font.FONT_WEIGHT_MAX) int mWeight = 0;
// Value for weight and italic. Indicates the value is resolved by font metadata.
// Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
@@ -673,6 +667,128 @@
}
/**
+ * A builder class for creating new Typeface instance.
+ *
+ * <p>
+ * Examples,
+ * 1) Create Typeface from single ttf file.
+ * <pre>
+ * <code>
+ * Font font = new Font.Builder("your_font_file.ttf").build();
+ * FontFamily family = new FontFamily.Builder(font).build();
+ * Typeface typeface = new Typeface.CustomFallbackBuilder(family).build();
+ * </code>
+ * </pre>
+ *
+ * 2) Create Typeface from multiple font files and select bold style by default.
+ * <pre>
+ * <code>
+ * Font regularFont = new Font.Builder("regular.ttf").build();
+ * Font boldFont = new Font.Builder("bold.ttf").build();
+ * FontFamily family = new FontFamily.Builder(regularFont)
+ * .addFont(boldFont).build();
+ * Typeface typeface = new Typeface.CustomFallbackBuilder(family)
+ * .setWeight(Font.FONT_WEIGHT_BOLD) // Set bold style as the default style.
+ * // If the font family doesn't have bold style font,
+ * // system will select the closest font.
+ * .build();
+ * </code>
+ * </pre>
+ *
+ * 3) Create Typeface from single ttf file and if that font does not have glyph for the
+ * characters, use "serif" font family instead.
+ * <pre>
+ * <code>
+ * Font font = new Font.Builder("your_font_file.ttf").build();
+ * FontFamily family = new FontFamily.Builder(font).build();
+ * Typeface typeface = new Typeface.CustomFallbackBuilder(family)
+ * .setFallback("serif") // Set serif font family as the fallback.
+ * .build();
+ * </code>
+ * </pre>
+ * </p>
+ */
+ public static class CustomFallbackBuilder {
+ // TODO: Remove package modifier once android.graphics.FontFamily is deprecated.
+ private final android.graphics.fonts.FontFamily mFamily;
+ private String mFallbackName = null;
+ private @IntRange(from = 0, to = 1000) int mWeight = 400;
+ private boolean mItalic = false;
+
+ /**
+ * Constructs a builder with a font family.
+ *
+ * @param family a family object
+ */
+ // TODO: Remove package modifier once android.graphics.FontFamily is deprecated.
+ public CustomFallbackBuilder(@NonNull android.graphics.fonts.FontFamily family) {
+ Preconditions.checkNotNull(family);
+ mFamily = family;
+ }
+
+ /**
+ * Sets a system fallback by name.
+ *
+ * @param familyName a family name to be used for fallback if the provided fonts can not be
+ * used
+ */
+ public CustomFallbackBuilder setFallback(@NonNull String familyName) {
+ Preconditions.checkNotNull(familyName);
+ mFallbackName = familyName;
+ return this;
+ }
+
+ /**
+ * Sets a weight of the Typeface.
+ *
+ * If the font family doesn't have a font of given weight, system will select the closest
+ * font from font family. For example, if a font family has fonts of 300 weight and 700
+ * weight then setWeight(400) is called, system will select the font of 300 weight.
+ *
+ * @see Font#FONT_WEIGHT_THIN
+ * @see Font#FONT_WEIGHT_EXTRA_LIGHT
+ * @see Font#FONT_WEIGHT_LIGHT
+ * @see Font#FONT_WEIGHT_NORMAL
+ * @see Font#FONT_WEIGHT_MEDIUM
+ * @see Font#FONT_WEIGHT_SEMI_BOLD
+ * @see Font#FONT_WEIGHT_BOLD
+ * @see Font#FONT_WEIGHT_EXTRA_BOLD
+ * @see Font#FONT_WEIGHT_BLACK
+ * @param weight a weight value
+ */
+ public CustomFallbackBuilder setWeight(@IntRange(from = 0, to = 1000) int weight) {
+ mWeight = weight;
+ return this;
+ }
+
+ /**
+ * Sets a italic style of the Typeface.
+ *
+ * @param italic true if italic, otherwise false
+ */
+ public CustomFallbackBuilder setItalic(boolean italic) {
+ mItalic = italic;
+ return this;
+ }
+
+ /**
+ * Create the Typeface based on the configured values.
+ *
+ * @return the Typeface object
+ */
+ public Typeface build() {
+ final android.graphics.fonts.FontFamily[] fallback =
+ SystemFonts.getSystemFallback(mFallbackName);
+ final long[] ptrArray = new long[fallback.length + 1];
+ ptrArray[0] = mFamily.getNativePtr();
+ for (int i = 0; i < fallback.length; ++i) {
+ ptrArray[i + 1] = fallback[i].getNativePtr();
+ }
+ return new Typeface(nativeCreateFromArray(ptrArray, mWeight, mItalic ? 1 : 0));
+ }
+ }
+
+ /**
* Create a typeface object given a family name, and option style information.
* If null is passed for the name, then the "default" font will be chosen.
* The resulting typeface object can be queried (getStyle()) to discover what
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index accc081..a337773 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -21,6 +21,7 @@
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -568,7 +569,7 @@
* Version of createWithResource that takes Resources. Do not use.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public static Icon createWithResource(Resources res, @DrawableRes int resId) {
if (res == null) {
throw new IllegalArgumentException("Resource must not be null.");
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 8de8f81..2855227 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -280,7 +280,7 @@
* @see #getStateDrawable(int)
* @see #getStateSet(int)
*/
- public int getStateDrawableIndex(int[] stateSet) {
+ public int findStateDrawableIndex(int[] stateSet) {
return mStateListState.indexOfStateSet(stateSet);
}
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index f0c5199..8aa4845 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.os.LocaleList;
import android.util.TypedValue;
import com.android.internal.util.Preconditions;
@@ -50,6 +51,11 @@
private static final int STYLE_NORMAL = 0;
/**
+ * A minimum weight value for the font
+ */
+ public static final int FONT_WEIGHT_MIN = 1;
+
+ /**
* A font weight value for the thin weight
*/
public static final int FONT_WEIGHT_THIN = 100;
@@ -95,6 +101,11 @@
public static final int FONT_WEIGHT_BLACK = 900;
/**
+ * A maximum weight value for the font
+ */
+ public static final int FONT_WEIGHT_MAX = 1000;
+
+ /**
* A builder class for creating new Font.
*/
public static class Builder {
@@ -107,6 +118,8 @@
nGetReleaseNativeFont(), 64);
private @Nullable ByteBuffer mBuffer;
+ private @Nullable File mFile;
+ private @NonNull LocaleList mLocaleList = LocaleList.getEmptyLocaleList();
private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED;
private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED;
private @IntRange(from = 0) int mTtcIndex = 0;
@@ -131,6 +144,19 @@
}
/**
+ * Construct a builder with a byte buffer and file path.
+ *
+ * This method is intended to be called only from SystemFonts.
+ * @hide
+ */
+ public Builder(@NonNull ByteBuffer buffer, @NonNull File path,
+ @NonNull LocaleList localeList) {
+ this(buffer);
+ mFile = path;
+ mLocaleList = localeList;
+ }
+
+ /**
* Constructs a builder with a file path.
*
* @param path a file path to the font file
@@ -143,6 +169,7 @@
} catch (IOException e) {
mException = e;
}
+ mFile = path;
}
/**
@@ -305,8 +332,9 @@
* @param weight a weight value
* @return this builder
*/
- public @NonNull Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
- Preconditions.checkArgument(1 <= weight && weight <= 1000);
+ public @NonNull Builder setWeight(
+ @IntRange(from = FONT_WEIGHT_MIN, to = FONT_WEIGHT_MAX) int weight) {
+ Preconditions.checkArgument(FONT_WEIGHT_MIN <= weight && weight <= FONT_WEIGHT_MAX);
mWeight = weight;
return this;
}
@@ -386,6 +414,7 @@
mItalic = STYLE_NORMAL;
}
}
+ mWeight = Math.max(FONT_WEIGHT_MIN, Math.min(FONT_WEIGHT_MAX, mWeight));
final boolean italic = (mItalic == STYLE_ITALIC);
final long builderPtr = nInitBuilder();
if (mAxes != null) {
@@ -394,7 +423,8 @@
}
}
final long ptr = nBuild(builderPtr, mBuffer, mWeight, italic, mTtcIndex);
- final Font font = new Font(ptr, mBuffer, mWeight, italic, mTtcIndex, mAxes);
+ final Font font = new Font(ptr, mBuffer, mFile, mWeight, italic, mTtcIndex, mAxes,
+ mLocaleList);
sFontRegistory.registerNativeAllocation(font, ptr);
return font;
}
@@ -422,23 +452,48 @@
private final long mNativePtr; // address of the shared ptr of minikin::Font
private final @NonNull ByteBuffer mBuffer;
+ private final @Nullable File mFile;
private final @IntRange(from = 0, to = 1000) int mWeight;
private final boolean mItalic;
private final @IntRange(from = 0) int mTtcIndex;
private final @Nullable FontVariationAxis[] mAxes;
+ private final @NonNull LocaleList mLocaleList;
/**
* Use Builder instead
*/
- private Font(long nativePtr, @NonNull ByteBuffer buffer,
+ private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file,
@IntRange(from = 0, to = 1000) int weight, boolean italic,
- @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes) {
+ @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes,
+ @NonNull LocaleList localeList) {
mBuffer = buffer;
+ mFile = file;
mWeight = weight;
mItalic = italic;
mNativePtr = nativePtr;
mTtcIndex = ttcIndex;
mAxes = axes;
+ mLocaleList = localeList;
+ }
+
+ /**
+ * Retuns a font file buffer.
+ *
+ * @return a font buffer
+ */
+ public @NonNull ByteBuffer getBuffer() {
+ return mBuffer;
+ }
+
+ /**
+ * Returns a file path of this font.
+ *
+ * This returns null if this font is not created from regular file.
+ *
+ * @return a file path of the font
+ */
+ public @Nullable File getFile() {
+ return mFile;
}
/**
@@ -484,6 +539,16 @@
return mAxes == null ? null : mAxes.clone();
}
+ /**
+ * Get a locale list of this font.
+ *
+ * This is always empty if this font is not a system font.
+ * @return a locale list
+ */
+ public @NonNull LocaleList getLocaleList() {
+ return mLocaleList;
+ }
+
/** @hide */
public long getNativePtr() {
return mNativePtr;
@@ -504,11 +569,19 @@
@Override
public int hashCode() {
- return Objects.hash(mWeight, mItalic, mTtcIndex, mAxes, mBuffer);
+ return Objects.hash(mWeight, mItalic, mTtcIndex, Arrays.hashCode(mAxes), mBuffer);
}
@Override
public String toString() {
- return "Font {weight=" + mWeight + ", italic=" + mItalic + "}";
+ return "Font {"
+ + "path=" + mFile
+ + ", weight=" + mWeight
+ + ", italic=" + mItalic
+ + ", ttcIndex=" + mTtcIndex
+ + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes)
+ + ", localeList=" + mLocaleList.toLanguageTags()
+ + ", buffer=" + mBuffer
+ + "}";
}
}
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index c914ece..3bcdc31 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -18,9 +18,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.text.FontConfig;
-import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -110,25 +108,16 @@
* @return a font family
*/
public @NonNull FontFamily build() {
- return build(null, FontConfig.Family.VARIANT_DEFAULT);
+ return build("", FontConfig.Family.VARIANT_DEFAULT);
}
/** @hide */
- public @NonNull FontFamily build(@Nullable String[] langTags, int variant) {
+ public @NonNull FontFamily build(@NonNull String langTags, int variant) {
final long builderPtr = nInitBuilder();
for (int i = 0; i < mFonts.size(); ++i) {
nAddFont(builderPtr, mFonts.get(i).getNativePtr());
}
- final String langString;
- if (langTags == null || langTags.length == 0) {
- langString = null;
- } else if (langTags.length == 1) {
- langString = langTags[0];
- } else {
- langString = TextUtils.join(",", langTags);
- }
-
- final long ptr = nBuild(builderPtr, langString, variant);
+ final long ptr = nBuild(builderPtr, langTags, variant);
final FontFamily family = new FontFamily(mFonts, ptr);
sFamilyRegistory.registerNativeAllocation(family, ptr);
return family;
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index 26b4ec5..1c957b8 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.FontListParser;
+import android.os.LocaleList;
import android.text.FontConfig;
import android.util.ArrayMap;
import android.util.Log;
@@ -28,6 +29,7 @@
import org.xmlpull.v1.XmlPullParserException;
+import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -36,12 +38,13 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Provides the system font configurations.
- * @hide
*/
public class SystemFonts {
private static final String TAG = "SystemFonts";
@@ -49,8 +52,19 @@
private SystemFonts() {} // Do not instansiate.
- static final Map<String, FontFamily[]> sSystemFallbackMap;
- static final FontConfig.Alias[] sAliases;
+ private static final Map<String, FontFamily[]> sSystemFallbackMap;
+ private static final FontConfig.Alias[] sAliases;
+ private static final Set<Font> sAvailableFonts;
+
+ /**
+ * Returns all available font files in the system.
+ *
+ * Note: The order of this font doesn't indicates anything.
+ * @return an array of system fonts
+ */
+ public static @NonNull Set<Font> getAvailableFonts() {
+ return sAvailableFonts;
+ }
/**
* Returns fallback list for the given family name.
@@ -58,6 +72,7 @@
* If no fallback found for the given family name, returns fallback for the default family.
*
* @param familyName family name, e.g. "serif"
+ * @hide
*/
public static @NonNull FontFamily[] getSystemFallback(@Nullable String familyName) {
final FontFamily[] families = sSystemFallbackMap.get(familyName);
@@ -68,6 +83,7 @@
* Returns raw system fallback map.
*
* This method is intended to be used only by Typeface static initializer.
+ * @hide
*/
public static @NonNull Map<String, FontFamily[]> getRawSystemFallbackMap() {
return sSystemFallbackMap;
@@ -77,6 +93,7 @@
* Returns a list of aliases.
*
* This method is intended to be used only by Typeface static initializer.
+ * @hide
*/
public static @NonNull FontConfig.Alias[] getAliases() {
return sAliases;
@@ -96,9 +113,10 @@
private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily,
@NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
@NonNull Map<String, ByteBuffer> cache,
- @NonNull String fontDir) {
+ @NonNull String fontDir,
+ @NonNull HashSet<Font> availableFonts) {
- final String[] languageTags = xmlFamily.getLanguages();
+ final String languageTags = xmlFamily.getLanguages();
final int variant = xmlFamily.getVariant();
final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>();
@@ -120,7 +138,8 @@
}
final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
- xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir);
+ xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir,
+ availableFonts);
// Insert family into fallback map.
for (int i = 0; i < fallbackMap.size(); i++) {
@@ -132,7 +151,8 @@
}
} else {
final FontFamily family = createFontFamily(
- xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir);
+ xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir,
+ availableFonts);
if (family != null) {
fallbackMap.valueAt(i).add(family);
} else if (defaultFamily != null) {
@@ -146,15 +166,17 @@
private static @Nullable FontFamily createFontFamily(@NonNull String familyName,
@NonNull List<FontConfig.Font> fonts,
- @NonNull String[] languageTags,
+ @NonNull String languageTags,
@FontConfig.Family.Variant int variant,
@NonNull Map<String, ByteBuffer> cache,
- @NonNull String fontDir) {
+ @NonNull String fontDir,
+ @NonNull HashSet<Font> availableFonts) {
if (fonts.size() == 0) {
return null;
}
FontFamily.Builder b = null;
+ final LocaleList localeList = LocaleList.forLanguageTags(languageTags);
for (int i = 0; i < fonts.size(); i++) {
final FontConfig.Font fontConfig = fonts.get(i);
final String fullPath = fontDir + fontConfig.getFontName();
@@ -172,7 +194,7 @@
final Font font;
try {
- font = new Font.Builder(buffer)
+ font = new Font.Builder(buffer, new File(fullPath), localeList)
.setWeight(fontConfig.getWeight())
.setItalic(fontConfig.isItalic())
.setTtcIndex(fontConfig.getTtcIndex())
@@ -182,6 +204,7 @@
throw new RuntimeException(e); // Never reaches here
}
+ availableFonts.add(font);
if (b == null) {
b = new FontFamily.Builder(font);
} else {
@@ -204,7 +227,8 @@
@VisibleForTesting
public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath,
@NonNull String fontDir,
- @NonNull ArrayMap<String, FontFamily[]> fallbackMap) {
+ @NonNull ArrayMap<String, FontFamily[]> fallbackMap,
+ @NonNull HashSet<Font> availableFonts) {
try {
final FileInputStream fontsIn = new FileInputStream(xmlPath);
final FontConfig fontConfig = FontListParser.parse(fontsIn);
@@ -221,7 +245,8 @@
}
final FontFamily family = createFontFamily(
xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()),
- xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir);
+ xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir,
+ availableFonts);
if (family == null) {
continue;
}
@@ -236,7 +261,8 @@
// The first family (usually the sans-serif family) is always placed immediately
// after the primary family in the fallback.
if (i == 0 || xmlFamily.getName() == null) {
- pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir);
+ pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir,
+ availableFonts);
}
}
@@ -258,9 +284,11 @@
static {
final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
+ final HashSet<Font> availableFonts = new HashSet<>();
sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/",
- systemFallbackMap);
+ systemFallbackMap, availableFonts);
sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
+ sAvailableFonts = Collections.unmodifiableSet(availableFonts);
}
}
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 78dbb6a..5d5e40f 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -40,6 +40,7 @@
import java.io.ByteArrayInputStream;
import java.io.Closeable;
+import java.io.Serializable;
import java.security.KeyPair;
import java.security.Principal;
import java.security.PrivateKey;
@@ -55,6 +56,8 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
+import javax.security.auth.x500.X500Principal;
+
import com.android.org.conscrypt.TrustedCertificateStore;
/**
@@ -142,6 +145,18 @@
public static final String EXTRA_SENDER = "sender";
/**
+ * Extra for use with {@link #ACTION_CHOOSER}
+ * @hide Also used by KeyChainActivity implementation
+ */
+ public static final String EXTRA_KEY_TYPES = "key_types";
+
+ /**
+ * Extra for use with {@link #ACTION_CHOOSER}
+ * @hide Also used by KeyChainActivity implementation
+ */
+ public static final String EXTRA_ISSUERS = "issuers";
+
+ /**
* Action to bring up the CertInstaller.
*/
private static final String ACTION_INSTALL = "android.credentials.INSTALL";
@@ -365,9 +380,10 @@
* onChoosePrivateKeyAlias}.
*
* <p>{@code keyTypes} and {@code issuers} may be used to
- * highlight suggested choices to the user, although to cope with
- * sometimes erroneous values provided by servers, the user may be
- * able to override these suggestions.
+ * narrow down suggested choices to the user. If either {@code keyTypes}
+ * or {@code issuers} is specified and non-empty, and there are no
+ * matching certificates in the KeyChain, then the certificate
+ * selection prompt would be suppressed entirely.
*
* <p>{@code host} and {@code port} may be used to give the user
* more context about the server requesting the credentials.
@@ -382,7 +398,7 @@
* @param response Callback to invoke when the request completes;
* must not be null.
* @param keyTypes The acceptable types of asymmetric keys such as
- * "RSA" or "DSA", or null.
+ * "RSA", "EC" or null.
* @param issuers The acceptable certificate issuers for the
* certificate matching the private key, or null.
* @param host The host name of the server requesting the
@@ -419,9 +435,10 @@
* onChoosePrivateKeyAlias}.
*
* <p>{@code keyTypes} and {@code issuers} may be used to
- * highlight suggested choices to the user, although to cope with
- * sometimes erroneous values provided by servers, the user may be
- * able to override these suggestions.
+ * narrow down suggested choices to the user. If either {@code keyTypes}
+ * or {@code issuers} is specified and non-empty, and there are no
+ * matching certificates in the KeyChain, then the certificate
+ * selection prompt would be suppressed entirely.
*
* <p>{@code uri} may be used to give the user more context about
* the server requesting the credentials.
@@ -436,13 +453,15 @@
* @param response Callback to invoke when the request completes;
* must not be null.
* @param keyTypes The acceptable types of asymmetric keys such as
- * "EC" or "RSA", or null.
+ * "RSA", "EC" or null.
* @param issuers The acceptable certificate issuers for the
* certificate matching the private key, or null.
* @param uri The full URI the server is requesting the certificate
* for, or null if unavailable.
* @param alias The alias to preselect if available, or null if
* unavailable.
+ * @throws IllegalArgumentException if the specified issuers are not
+ * of type {@code X500Principal}.
*/
public static void choosePrivateKeyAlias(@NonNull Activity activity,
@NonNull KeyChainAliasCallback response,
@@ -450,20 +469,21 @@
@Nullable Principal[] issuers,
@Nullable Uri uri, @Nullable String alias) {
/*
- * TODO currently keyTypes, issuers are unused. They are meant
- * to follow the semantics and purpose of X509KeyManager
- * method arguments.
+ * Specifying keyTypes excludes certificates with different key types
+ * from the list of certificates presented to the user.
+ * In practice today, most servers would require RSA or EC
+ * certificates.
*
- * keyTypes would allow the list to be filtered and typically
- * will be set correctly by the server. In practice today,
- * most all users will want only RSA or EC, and usually
- * only a small number of certs will be available.
- *
- * issuers is typically not useful. Some servers historically
- * will send the entire list of public CAs known to the
- * server. Others will send none. If this is used, if there
- * are no matches after applying the constraint, it should be
- * ignored.
+ * Specifying issuers narrows down the list by filtering out
+ * certificates with issuers which are not matching the provided ones.
+ * This has been reported to Chrome several times (crbug.com/731769).
+ * There's no concrete description on what to do when the client has no
+ * certificates that match the provided issuers.
+ * To be conservative, Android will not present the user with any
+ * certificates to choose from.
+ * If the list of issuers is empty then the client may send any
+ * certificate, see:
+ * https://tools.ietf.org/html/rfc5246#section-7.4.4
*/
if (activity == null) {
throw new NullPointerException("activity == null");
@@ -476,6 +496,26 @@
intent.putExtra(EXTRA_RESPONSE, new AliasResponse(response));
intent.putExtra(EXTRA_URI, uri);
intent.putExtra(EXTRA_ALIAS, alias);
+ intent.putExtra(EXTRA_KEY_TYPES, keyTypes);
+ ArrayList<byte[]> issuersList = new ArrayList();
+ if (issuers != null) {
+ for (Principal issuer: issuers) {
+ // In a TLS client context (like Chrome), issuers would only
+ // be specified as X500Principals. No other use cases for
+ // specifying principals have been brought up. Under these
+ // circumstances, only allow issuers specified as
+ // X500Principals.
+ if (!(issuer instanceof X500Principal)) {
+ throw new IllegalArgumentException(String.format(
+ "Issuer %s is of type %s, not X500Principal",
+ issuer.toString(), issuer.getClass()));
+ }
+ // Pass the DER-encoded issuer as that's the most accurate
+ // representation and what is sent over the wire.
+ issuersList.add(((X500Principal) issuer).getEncoded());
+ }
+ }
+ intent.putExtra(EXTRA_ISSUERS, (Serializable) issuersList);
// the PendingIntent is used to get calling package name
intent.putExtra(EXTRA_SENDER, PendingIntent.getActivity(activity, 0, new Intent(), 0));
activity.startActivity(intent);
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 402e390..dc4a0a7 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3205,20 +3205,6 @@
break;
}
}
- if ((colorMode&MASK_HDR) != 0) {
- if (res.size() > 0) res.append("-");
- switch (colorMode&MASK_HDR) {
- case ResTable_config::HDR_NO:
- res.append("lowdr");
- break;
- case ResTable_config::HDR_YES:
- res.append("highdr");
- break;
- default:
- res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR));
- break;
- }
- }
if ((colorMode&MASK_WIDE_COLOR_GAMUT) != 0) {
if (res.size() > 0) res.append("-");
switch (colorMode&MASK_WIDE_COLOR_GAMUT) {
@@ -3233,6 +3219,20 @@
break;
}
}
+ if ((colorMode&MASK_HDR) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (colorMode&MASK_HDR) {
+ case ResTable_config::HDR_NO:
+ res.append("lowdr");
+ break;
+ case ResTable_config::HDR_YES:
+ res.append("highdr");
+ break;
+ default:
+ res.appendFormat("hdr=%d", dtohs(colorMode&MASK_HDR));
+ break;
+ }
+ }
if (orientation != ORIENTATION_ANY) {
if (res.size() > 0) res.append("-");
switch (orientation) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index d582983..04bbe24 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -72,6 +72,7 @@
"libft2",
"libminikin",
"libandroidfw",
+ "libcrypto",
],
static_libs: [
"libEGL_blobCache",
@@ -169,15 +170,12 @@
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaMemoryTracer.cpp",
"pipeline/skia/SkiaOpenGLPipeline.cpp",
- "pipeline/skia/SkiaOpenGLReadback.cpp",
"pipeline/skia/SkiaPipeline.cpp",
"pipeline/skia/SkiaProfileRenderer.cpp",
"pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/SkiaVulkanPipeline.cpp",
"pipeline/skia/VectorDrawableAtlas.cpp",
- "renderstate/PixelBufferState.cpp",
"renderstate/RenderState.cpp",
- "renderstate/TextureState.cpp",
"renderthread/CacheManager.cpp",
"renderthread/CanvasContext.cpp",
"renderthread/DrawFrameTask.cpp",
@@ -189,18 +187,19 @@
"renderthread/TimeLord.cpp",
"renderthread/Frame.cpp",
"service/GraphicsStatsService.cpp",
+ "surfacetexture/EGLConsumer.cpp",
+ "surfacetexture/ImageConsumer.cpp",
+ "surfacetexture/SurfaceTexture.cpp",
"thread/TaskManager.cpp",
"utils/Blur.cpp",
"utils/Color.cpp",
"utils/GLUtils.cpp",
"utils/LinearAllocator.cpp",
"utils/StringUtils.cpp",
- "utils/TestWindowContext.cpp",
"utils/VectorDrawableUtils.cpp",
"AnimationContext.cpp",
"Animator.cpp",
"AnimatorManager.cpp",
- "Caches.cpp",
"CanvasState.cpp",
"CanvasTransform.cpp",
"ClipArea.cpp",
@@ -209,7 +208,6 @@
"DeviceInfo.cpp",
"FrameInfo.cpp",
"FrameInfoVisualizer.cpp",
- "GlLayer.cpp",
"GpuMemoryTracker.cpp",
"HardwareBitmapUploader.cpp",
"Interpolator.cpp",
@@ -217,23 +215,20 @@
"Layer.cpp",
"LayerUpdateQueue.cpp",
"Matrix.cpp",
- "EglReadback.cpp",
"PathParser.cpp",
- "PixelBuffer.cpp",
"ProfileData.cpp",
"ProfileDataContainer.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
"PropertyValuesHolder.cpp",
+ "Readback.cpp",
"RecordingCanvas.cpp",
"RenderNode.cpp",
"RenderProperties.cpp",
"ResourceCache.cpp",
"SkiaCanvas.cpp",
"Snapshot.cpp",
- "Texture.cpp",
"VectorDrawable.cpp",
- "VkLayer.cpp",
"protos/graphicsstats.proto",
],
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
deleted file mode 100644
index 2541444..0000000
--- a/libs/hwui/Caches.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open 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 "Caches.h"
-
-#include "GlLayer.h"
-#include "Properties.h"
-#include "renderstate/RenderState.h"
-#include "utils/GLUtils.h"
-
-#include <cutils/properties.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-
-namespace android {
-namespace uirenderer {
-
-Caches* Caches::sInstance = nullptr;
-
-///////////////////////////////////////////////////////////////////////////////
-// Macros
-///////////////////////////////////////////////////////////////////////////////
-
-#if DEBUG_CACHE_FLUSH
-#define FLUSH_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-#define FLUSH_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Constructors/destructor
-///////////////////////////////////////////////////////////////////////////////
-
-Caches::Caches(RenderState& renderState) : mInitialized(false) {
- INIT_LOGD("Creating OpenGL renderer caches");
- init();
- initStaticProperties();
-}
-
-bool Caches::init() {
- if (mInitialized) return false;
-
- ATRACE_NAME("Caches::init");
-
- mRegionMesh = nullptr;
-
- mInitialized = true;
-
- mPixelBufferState = new PixelBufferState();
- mTextureState = new TextureState();
- mTextureState->constructTexture(*this);
-
- return true;
-}
-
-void Caches::initStaticProperties() {
- // OpenGL ES 3.0+ specific features
- gpuPixelBuffersEnabled = extensions().hasPixelBufferObjects() &&
- property_get_bool(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, true);
-}
-
-void Caches::terminate() {
- if (!mInitialized) return;
- mRegionMesh.reset(nullptr);
-
- clearGarbage();
-
- delete mPixelBufferState;
- mPixelBufferState = nullptr;
- delete mTextureState;
- mTextureState = nullptr;
- mInitialized = false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Memory management
-///////////////////////////////////////////////////////////////////////////////
-
-void Caches::clearGarbage() {}
-
-void Caches::flush(FlushMode mode) {
- clearGarbage();
- glFinish();
- // Errors during cleanup should be considered non-fatal, dump them and
- // and move on. TODO: All errors or just errors like bad surface?
- GLUtils::dumpGLErrors();
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
deleted file mode 100644
index 642f9dc..0000000
--- a/libs/hwui/Caches.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open 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.
- */
-
-#pragma once
-
-#include "DeviceInfo.h"
-#include "Extensions.h"
-#include "ResourceCache.h"
-#include "renderstate/PixelBufferState.h"
-#include "renderstate/TextureState.h"
-#include "thread/TaskManager.h"
-#include "thread/TaskProcessor.h"
-
-#include <memory>
-#include <vector>
-
-#include <GLES3/gl3.h>
-
-#include <utils/KeyedVector.h>
-
-#include <cutils/compiler.h>
-
-#include <SkPath.h>
-
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// Caches
-///////////////////////////////////////////////////////////////////////////////
-
-class RenderNode;
-class RenderState;
-
-class ANDROID_API Caches {
-public:
- static Caches& createInstance(RenderState& renderState) {
- LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted");
- sInstance = new Caches(renderState);
- return *sInstance;
- }
-
- static Caches& getInstance() {
- LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created");
- return *sInstance;
- }
-
- static bool hasInstance() { return sInstance != nullptr; }
-
-private:
- explicit Caches(RenderState& renderState);
- static Caches* sInstance;
-
-public:
- enum class FlushMode { Layers = 0, Moderate, Full };
-
- /**
- * Initialize caches.
- */
- bool init();
-
- bool isInitialized() { return mInitialized; }
-
- /**
- * Flush the cache.
- *
- * @param mode Indicates how much of the cache should be flushed
- */
- void flush(FlushMode mode);
-
- /**
- * Destroys all resources associated with this cache. This should
- * be called after a flush(FlushMode::Full).
- */
- void terminate();
-
- /**
- * Call this on each frame to ensure that garbage is deleted from
- * GPU memory.
- */
- void clearGarbage();
-
- /**
- * Returns the GL RGBA internal format to use for the current device
- * If the device supports linear blending and needSRGB is true,
- * this function returns GL_SRGB8_ALPHA8, otherwise it returns GL_RGBA
- */
- constexpr GLint rgbaInternalFormat(bool needSRGB = true) const {
- return extensions().hasLinearBlending() && needSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA;
- }
-
-public:
- TaskManager tasks;
-
- bool gpuPixelBuffersEnabled;
-
- const Extensions& extensions() const { return DeviceInfo::get()->extensions(); }
- PixelBufferState& pixelBufferState() { return *mPixelBufferState; }
- TextureState& textureState() { return *mTextureState; }
-
-private:
- void initStaticProperties();
-
- static void eventMarkNull(GLsizei length, const GLchar* marker) {}
- static void startMarkNull(GLsizei length, const GLchar* marker) {}
- static void endMarkNull() {}
-
- // Used to render layers
- std::unique_ptr<TextureVertex[]> mRegionMesh;
-
- bool mInitialized;
-
- // TODO: move below to RenderState
- PixelBufferState* mPixelBufferState = nullptr;
- TextureState* mTextureState = nullptr;
-
-}; // class Caches
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 569de76..837d546 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -15,27 +15,20 @@
*/
#include "DeferredLayerUpdater.h"
-#include "GlLayer.h"
-#include "VkLayer.h"
#include "renderstate/RenderState.h"
-#include "renderthread/EglManager.h"
-#include "renderthread/RenderTask.h"
#include "utils/PaintUtils.h"
namespace android {
namespace uirenderer {
-DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn,
- Layer::Api layerApi)
+DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState)
: mRenderState(renderState)
, mBlend(false)
, mSurfaceTexture(nullptr)
, mTransform(nullptr)
, mGLContextAttached(false)
, mUpdateTexImage(false)
- , mLayer(nullptr)
- , mLayerApi(layerApi)
- , mCreateLayerFn(createLayerFn) {
+ , mLayer(nullptr) {
renderState.registerDeferredLayerUpdater(this);
}
@@ -50,16 +43,13 @@
return;
}
- if (mSurfaceTexture.get() && mLayerApi == Layer::Api::OpenGL && mGLContextAttached) {
- status_t err = mSurfaceTexture->detachFromContext();
+ if (mSurfaceTexture.get() && mGLContextAttached) {
+ mSurfaceTexture->detachFromView();
mGLContextAttached = false;
- if (err != 0) {
- // TODO: Elevate to fatal exception
- ALOGE("Failed to detach SurfaceTexture from context %d", err);
- }
}
mLayer->postDecStrong();
+
mLayer = nullptr;
}
@@ -75,99 +65,53 @@
void DeferredLayerUpdater::apply() {
if (!mLayer) {
- mLayer = mCreateLayerFn(mRenderState, mWidth, mHeight, mColorFilter, mAlpha, mMode, mBlend);
+ mLayer = new Layer(mRenderState, mColorFilter, mAlpha, mMode);
}
mLayer->setColorFilter(mColorFilter);
mLayer->setAlpha(mAlpha, mMode);
if (mSurfaceTexture.get()) {
- if (mLayer->getApi() == Layer::Api::Vulkan) {
- if (mUpdateTexImage) {
- mUpdateTexImage = false;
- doUpdateVkTexImage();
- }
- } else {
- LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
- "apply surfaceTexture with non GL backend %x, GL %x, VK %x",
- mLayer->getApi(), Layer::Api::OpenGL, Layer::Api::Vulkan);
- if (!mGLContextAttached) {
- mGLContextAttached = true;
- mUpdateTexImage = true;
- mSurfaceTexture->attachToContext(static_cast<GlLayer*>(mLayer)->getTextureId());
- }
- if (mUpdateTexImage) {
- mUpdateTexImage = false;
- doUpdateTexImage();
- }
- GLenum renderTarget = mSurfaceTexture->getCurrentTextureTarget();
- static_cast<GlLayer*>(mLayer)->setRenderTarget(renderTarget);
+ if (!mGLContextAttached) {
+ mGLContextAttached = true;
+ mUpdateTexImage = true;
+ mSurfaceTexture->attachToView();
}
+ if (mUpdateTexImage) {
+ mUpdateTexImage = false;
+ sk_sp<SkImage> layerImage;
+ SkMatrix textureTransform;
+ android_dataspace dataSpace;
+ bool queueEmpty = true;
+ // If the SurfaceTexture queue is in synchronous mode, need to discard all
+ // but latest frame. Since we can't tell which mode it is in,
+ // do this unconditionally.
+ do {
+ layerImage = mSurfaceTexture->dequeueImage(textureTransform, dataSpace, &queueEmpty,
+ mRenderState);
+ } while (layerImage.get() && (!queueEmpty));
+ if (layerImage.get()) {
+ // force filtration if buffer size != layer size
+ bool forceFilter = mWidth != layerImage->width() || mHeight != layerImage->height();
+ updateLayer(forceFilter, textureTransform, dataSpace, layerImage);
+ }
+ }
+
if (mTransform) {
- mLayer->getTransform().load(*mTransform);
+ mLayer->getTransform() = *mTransform;
setTransform(nullptr);
}
}
}
-void DeferredLayerUpdater::doUpdateTexImage() {
- LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::OpenGL,
- "doUpdateTexImage non GL backend %x, GL %x, VK %x", mLayer->getApi(),
- Layer::Api::OpenGL, Layer::Api::Vulkan);
- if (mSurfaceTexture->updateTexImage() == NO_ERROR) {
- float transform[16];
-
- int64_t frameNumber = mSurfaceTexture->getFrameNumber();
- // If the GLConsumer queue is in synchronous mode, need to discard all
- // but latest frame, using the frame number to tell when we no longer
- // have newer frames to target. Since we can't tell which mode it is in,
- // do this unconditionally.
- int dropCounter = 0;
- while (mSurfaceTexture->updateTexImage() == NO_ERROR) {
- int64_t newFrameNumber = mSurfaceTexture->getFrameNumber();
- if (newFrameNumber == frameNumber) break;
- frameNumber = newFrameNumber;
- dropCounter++;
- }
-
- bool forceFilter = false;
- sp<GraphicBuffer> buffer = mSurfaceTexture->getCurrentBuffer();
- if (buffer != nullptr) {
- // force filtration if buffer size != layer size
- forceFilter = mWidth != static_cast<int>(buffer->getWidth()) ||
- mHeight != static_cast<int>(buffer->getHeight());
- }
-
-#if DEBUG_RENDERER
- if (dropCounter > 0) {
- RENDERER_LOGD("Dropped %d frames on texture layer update", dropCounter);
- }
-#endif
- mSurfaceTexture->getTransformMatrix(transform);
-
- updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace());
- }
-}
-
-void DeferredLayerUpdater::doUpdateVkTexImage() {
- LOG_ALWAYS_FATAL_IF(mLayer->getApi() != Layer::Api::Vulkan,
- "updateLayer non Vulkan backend %x, GL %x, VK %x", mLayer->getApi(),
- Layer::Api::OpenGL, Layer::Api::Vulkan);
-
- static const mat4 identityMatrix;
- updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN);
-
- VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
- vkLayer->updateTexture();
-}
-
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform,
- android_dataspace dataspace) {
+void DeferredLayerUpdater::updateLayer(bool forceFilter, const SkMatrix& textureTransform,
+ android_dataspace dataspace, const sk_sp<SkImage>& layerImage) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
- mLayer->getTexTransform().load(textureTransform);
+ mLayer->getTexTransform() = textureTransform;
mLayer->setDataSpace(dataspace);
+ mLayer->setImage(layerImage);
}
void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index fe3ee7a..4c323b8 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -17,18 +17,19 @@
#pragma once
#include <SkColorFilter.h>
+#include <SkImage.h>
#include <SkMatrix.h>
#include <cutils/compiler.h>
-#include <gui/GLConsumer.h>
+#include <map>
#include <system/graphics.h>
#include <utils/StrongPointer.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
+#include "surfacetexture/SurfaceTexture.h"
#include "Layer.h"
#include "Rect.h"
-#include "renderthread/RenderThread.h"
namespace android {
namespace uirenderer {
@@ -41,12 +42,7 @@
public:
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
// and will not call incrementRef on it as a result.
- typedef std::function<Layer*(RenderState& renderState, uint32_t layerWidth,
- uint32_t layerHeight, sk_sp<SkColorFilter> colorFilter, int alpha,
- SkBlendMode mode, bool blend)>
- CreateLayerFn;
- ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn,
- Layer::Api layerApi);
+ ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState);
ANDROID_API ~DeferredLayerUpdater();
@@ -70,13 +66,13 @@
return false;
}
- ANDROID_API void setSurfaceTexture(const sp<GLConsumer>& texture) {
- if (texture.get() != mSurfaceTexture.get()) {
- mSurfaceTexture = texture;
+ ANDROID_API void setSurfaceTexture(const sp<SurfaceTexture>& consumer) {
+ if (consumer.get() != mSurfaceTexture.get()) {
+ mSurfaceTexture = consumer;
- GLenum target = texture->getCurrentTextureTarget();
+ GLenum target = consumer->getCurrentTextureTarget();
LOG_ALWAYS_FATAL_IF(target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES,
- "set unsupported GLConsumer with target %x", target);
+ "set unsupported SurfaceTexture with target %x", target);
}
}
@@ -97,12 +93,11 @@
void detachSurfaceTexture();
- void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace);
+ void updateLayer(bool forceFilter, const SkMatrix& textureTransform,
+ android_dataspace dataspace, const sk_sp<SkImage>& layerImage);
void destroyLayer();
- Layer::Api getBackingLayerApi() { return mLayerApi; }
-
private:
RenderState& mRenderState;
@@ -113,17 +108,12 @@
sk_sp<SkColorFilter> mColorFilter;
int mAlpha = 255;
SkBlendMode mMode = SkBlendMode::kSrcOver;
- sp<GLConsumer> mSurfaceTexture;
+ sp<SurfaceTexture> mSurfaceTexture;
SkMatrix* mTransform;
bool mGLContextAttached;
bool mUpdateTexImage;
Layer* mLayer;
- Layer::Api mLayerApi;
- CreateLayerFn mCreateLayerFn;
-
- void doUpdateTexImage();
- void doUpdateVkTexImage();
};
} /* namespace uirenderer */
diff --git a/libs/hwui/EglReadback.cpp b/libs/hwui/EglReadback.cpp
deleted file mode 100644
index 65becf8..0000000
--- a/libs/hwui/EglReadback.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "EglReadback.h"
-
-#include "renderthread/EglManager.h"
-
-#include <gui/Surface.h>
-#include <ui/Fence.h>
-#include <ui/GraphicBuffer.h>
-
-namespace android {
-namespace uirenderer {
-
-CopyResult EglReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
- ATRACE_CALL();
- // Setup the source
- sp<GraphicBuffer> sourceBuffer;
- sp<Fence> sourceFence;
- Matrix4 texTransform;
- status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data);
- texTransform.invalidateType();
- if (err != NO_ERROR) {
- ALOGW("Failed to get last queued buffer, error = %d", err);
- return CopyResult::UnknownError;
- }
- if (!sourceBuffer.get()) {
- ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
- return CopyResult::SourceEmpty;
- }
- if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
- ALOGW("Surface is protected, unable to copy from it");
- return CopyResult::SourceInvalid;
- }
- err = sourceFence->wait(500 /* ms */);
- if (err != NO_ERROR) {
- ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
- return CopyResult::Timeout;
- }
-
- return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap);
-}
-
-CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform,
- const Rect& srcRect, SkBitmap* bitmap) {
- mRenderThread.requireGlContext();
- // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
- // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
- // to be able to properly sample from the buffer.
-
- // Create the EGLImage object that maps the GraphicBuffer
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
- EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
-
- EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- clientBuffer, attrs);
-
- if (sourceImage == EGL_NO_IMAGE_KHR) {
- ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
- return CopyResult::UnknownError;
- }
-
- uint32_t width = graphicBuffer->getWidth();
- uint32_t height = graphicBuffer->getHeight();
- CopyResult copyResult =
- copyImageInto(sourceImage, texTransform, width, height, srcRect, bitmap);
-
- eglDestroyImageKHR(display, sourceImage);
- return copyResult;
-}
-
-CopyResult EglReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) {
- Rect srcRect;
- Matrix4 transform;
- transform.loadScale(1, -1, 1);
- transform.translate(0, -1);
- return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap);
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/EglReadback.h b/libs/hwui/EglReadback.h
deleted file mode 100644
index e723169..0000000
--- a/libs/hwui/EglReadback.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Readback.h"
-
-#include "Matrix.h"
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-namespace android {
-namespace uirenderer {
-
-class EglReadback : public Readback {
-public:
- virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
- SkBitmap* bitmap) override;
- virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
- SkBitmap* bitmap) override;
-
-protected:
- explicit EglReadback(renderthread::RenderThread& thread) : Readback(thread) {}
- virtual ~EglReadback() {}
-
- virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect,
- SkBitmap* bitmap) = 0;
-
-private:
- CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, Matrix4& texTransform,
- const Rect& srcRect, SkBitmap* bitmap);
-};
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp
deleted file mode 100644
index 432bb85..0000000
--- a/libs/hwui/GlLayer.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "GlLayer.h"
-
-#include "Caches.h"
-#include "RenderNode.h"
-#include "renderstate/RenderState.h"
-
-namespace android {
-namespace uirenderer {
-
-GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
- : Layer(renderState, Api::OpenGL, colorFilter, alpha, mode)
- , caches(Caches::getInstance())
- , texture(caches) {
- texture.mWidth = layerWidth;
- texture.mHeight = layerHeight;
- texture.blend = blend;
-}
-
-GlLayer::~GlLayer() {
- // There's a rare possibility that Caches could have been destroyed already
- // since this method is queued up as a task.
- // Since this is a reset method, treat this as non-fatal.
- if (caches.isInitialized() && texture.mId) {
- texture.deleteTexture();
- }
-}
-
-void GlLayer::onGlContextLost() {
- texture.deleteTexture();
-}
-
-void GlLayer::setRenderTarget(GLenum renderTarget) {
- if (renderTarget != getRenderTarget()) {
- // new render target: bind with new target, and update filter/wrap
- texture.mTarget = renderTarget;
- if (texture.mId) {
- caches.textureState().bindTexture(texture.target(), texture.mId);
- }
- texture.setFilter(GL_NEAREST, false, true);
- texture.setWrap(GL_CLAMP_TO_EDGE, false, true);
- }
-}
-
-void GlLayer::generateTexture() {
- if (!texture.mId) {
- glGenTextures(1, &texture.mId);
- }
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h
deleted file mode 100644
index 9f70fda..0000000
--- a/libs/hwui/GlLayer.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Layer.h"
-
-#include "Texture.h"
-
-namespace android {
-namespace uirenderer {
-
-// Forward declarations
-class Caches;
-
-/**
- * A layer has dimensions and is backed by an OpenGL texture or FBO.
- */
-class GlLayer : public Layer {
-public:
- GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend);
- virtual ~GlLayer();
-
- uint32_t getWidth() const override { return texture.mWidth; }
-
- uint32_t getHeight() const override { return texture.mHeight; }
-
- void setSize(uint32_t width, uint32_t height) override {
- texture.updateLayout(width, height, texture.internalFormat(), texture.format(),
- texture.target());
- }
-
- void setBlend(bool blend) override { texture.blend = blend; }
-
- bool isBlend() const override { return texture.blend; }
-
- inline GLuint getTextureId() const { return texture.id(); }
-
- inline GLenum getRenderTarget() const { return texture.target(); }
-
- void setRenderTarget(GLenum renderTarget);
-
- void generateTexture();
-
- /**
- * Lost the GL context but the layer is still around, mark it invalid internally
- * so the dtor knows not to do any GL work
- */
- void onGlContextLost();
-
-private:
- Caches& caches;
-
- /**
- * The texture backing this layer.
- */
- Texture texture;
-}; // struct GlLayer
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp
index 612bfde..a9a7af8 100644
--- a/libs/hwui/GpuMemoryTracker.cpp
+++ b/libs/hwui/GpuMemoryTracker.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include "Texture.h"
#include "utils/StringUtils.h"
#include <GpuMemoryTracker.h>
@@ -117,22 +116,6 @@
ATRACE_INT(buf, stats.count);
}
}
-
- std::vector<const Texture*> freeList;
- for (const auto& obj : gObjectSet) {
- if (obj->objectType() == GpuObjectType::Texture) {
- const Texture* texture = static_cast<Texture*>(obj);
- if (texture->cleanup) {
- ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", texture->id(),
- texture->width(), texture->height());
- freeList.push_back(texture);
- }
- }
- }
- for (auto& texture : freeList) {
- const_cast<Texture*>(texture)->deleteTexture();
- delete texture;
- }
}
} // namespace uirenderer
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index fb8f033..cc95051 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -17,17 +17,16 @@
#include "Layer.h"
#include "renderstate/RenderState.h"
+#include "utils/Color.h"
#include <SkToSRGBColorFilter.h>
namespace android {
namespace uirenderer {
-Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> colorFilter, int alpha,
- SkBlendMode mode)
- : GpuMemoryTracker(GpuObjectType::Layer)
- , mRenderState(renderState)
- , mApi(api)
+Layer::Layer(RenderState& renderState, sk_sp<SkColorFilter> colorFilter, int alpha,
+ SkBlendMode mode)
+ : mRenderState(renderState)
, mColorFilter(colorFilter)
, alpha(alpha)
, mode(mode) {
@@ -36,6 +35,8 @@
incStrong(nullptr);
buildColorSpaceWithFilter();
renderState.registerLayer(this);
+ texTransform.setIdentity();
+ transform.setIdentity();
}
Layer::~Layer() {
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 31878ac..6f07a43 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -16,15 +16,15 @@
#pragma once
-#include <GpuMemoryTracker.h>
#include <utils/RefBase.h>
#include <SkBlendMode.h>
#include <SkColorFilter.h>
#include <SkColorSpace.h>
#include <SkPaint.h>
-
-#include "Matrix.h"
+#include <SkImage.h>
+#include <SkMatrix.h>
+#include <system/graphics.h>
namespace android {
namespace uirenderer {
@@ -38,26 +38,21 @@
/**
* A layer has dimensions and is backed by a backend specific texture or framebuffer.
*/
-class Layer : public VirtualLightRefBase, GpuMemoryTracker {
+class Layer : public VirtualLightRefBase {
public:
- enum class Api {
- OpenGL = 0,
- Vulkan = 1,
- };
-
- Api getApi() const { return mApi; }
+ Layer(RenderState& renderState, sk_sp<SkColorFilter>, int alpha, SkBlendMode mode);
~Layer();
- virtual uint32_t getWidth() const = 0;
+ uint32_t getWidth() const { return mWidth; }
- virtual uint32_t getHeight() const = 0;
+ uint32_t getHeight() const { return mHeight; }
- virtual void setSize(uint32_t width, uint32_t height) = 0;
+ void setSize(uint32_t width, uint32_t height) { mWidth = width; mHeight = height; }
- virtual void setBlend(bool blend) = 0;
+ void setBlend(bool blend) { mBlend = blend; }
- virtual bool isBlend() const = 0;
+ bool isBlend() const { return mBlend; }
inline void setForceFilter(bool forceFilter) { this->forceFilter = forceFilter; }
@@ -84,9 +79,9 @@
inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
- inline mat4& getTexTransform() { return texTransform; }
+ inline SkMatrix& getTexTransform() { return texTransform; }
- inline mat4& getTransform() { return transform; }
+ inline SkMatrix& getTransform() { return transform; }
/**
* Posts a decStrong call to the appropriate thread.
@@ -94,16 +89,17 @@
*/
void postDecStrong();
+ inline void setImage(const sk_sp<SkImage>& image) { this->layerImage = image; }
+
+ inline sk_sp<SkImage> getImage() const { return this->layerImage; }
+
protected:
- Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha, SkBlendMode mode);
RenderState& mRenderState;
private:
void buildColorSpaceWithFilter();
- Api mApi;
-
/**
* Color filter used to draw this layer. Optional.
*/
@@ -137,12 +133,32 @@
/**
* Optional texture coordinates transform.
*/
- mat4 texTransform;
+ SkMatrix texTransform;
/**
* Optional transform.
*/
- mat4 transform;
+ SkMatrix transform;
+
+ /**
+ * An image backing the layer.
+ */
+ sk_sp<SkImage> layerImage;
+
+ /**
+ * layer width.
+ */
+ uint32_t mWidth = 0;
+
+ /**
+ * layer height.
+ */
+ uint32_t mHeight = 0;
+
+ /**
+ * enable blending
+ */
+ bool mBlend = false;
}; // struct Layer
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
deleted file mode 100644
index 910a988..0000000
--- a/libs/hwui/PixelBuffer.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open 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 "PixelBuffer.h"
-
-#include "Debug.h"
-#include "Extensions.h"
-#include "Properties.h"
-#include "renderstate/RenderState.h"
-#include "utils/GLUtils.h"
-
-#include <utils/Log.h>
-
-namespace android {
-namespace uirenderer {
-
-///////////////////////////////////////////////////////////////////////////////
-// CPU pixel buffer
-///////////////////////////////////////////////////////////////////////////////
-
-class CpuPixelBuffer : public PixelBuffer {
-public:
- CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
-
- uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
-
- void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
-
-protected:
- void unmap() override;
-
-private:
- std::unique_ptr<uint8_t[]> mBuffer;
-};
-
-CpuPixelBuffer::CpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
- : PixelBuffer(format, width, height)
- , mBuffer(new uint8_t[width * height * formatSize(format)]) {}
-
-uint8_t* CpuPixelBuffer::map(AccessMode mode) {
- if (mAccessMode == kAccessMode_None) {
- mAccessMode = mode;
- }
- return mBuffer.get();
-}
-
-void CpuPixelBuffer::unmap() {
- mAccessMode = kAccessMode_None;
-}
-
-void CpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE,
- &mBuffer[offset]);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// GPU pixel buffer
-///////////////////////////////////////////////////////////////////////////////
-
-class GpuPixelBuffer : public PixelBuffer {
-public:
- GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height);
- ~GpuPixelBuffer();
-
- uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) override;
-
- void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) override;
-
-protected:
- void unmap() override;
-
-private:
- GLuint mBuffer;
- uint8_t* mMappedPointer;
- Caches& mCaches;
-};
-
-GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
- : PixelBuffer(format, width, height)
- , mMappedPointer(nullptr)
- , mCaches(Caches::getInstance()) {
- glGenBuffers(1, &mBuffer);
-
- mCaches.pixelBufferState().bind(mBuffer);
- glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW);
- mCaches.pixelBufferState().unbind();
-}
-
-GpuPixelBuffer::~GpuPixelBuffer() {
- glDeleteBuffers(1, &mBuffer);
-}
-
-uint8_t* GpuPixelBuffer::map(AccessMode mode) {
- if (mAccessMode == kAccessMode_None) {
- mCaches.pixelBufferState().bind(mBuffer);
- mMappedPointer = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
- if (CC_UNLIKELY(!mMappedPointer)) {
- GLUtils::dumpGLErrors();
- LOG_ALWAYS_FATAL("Failed to map PBO");
- }
- mAccessMode = mode;
- mCaches.pixelBufferState().unbind();
- }
-
- return mMappedPointer;
-}
-
-void GpuPixelBuffer::unmap() {
- if (mAccessMode != kAccessMode_None) {
- if (mMappedPointer) {
- mCaches.pixelBufferState().bind(mBuffer);
- GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
- if (status == GL_FALSE) {
- ALOGE("Corrupted GPU pixel buffer");
- }
- }
- mAccessMode = kAccessMode_None;
- mMappedPointer = nullptr;
- }
-}
-
-void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
- // If the buffer is not mapped, unmap() will not bind it
- mCaches.pixelBufferState().bind(mBuffer);
- unmap();
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat, GL_UNSIGNED_BYTE,
- reinterpret_cast<void*>(offset));
- mCaches.pixelBufferState().unbind();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Factory
-///////////////////////////////////////////////////////////////////////////////
-
-PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
- if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
- return new GpuPixelBuffer(format, width, height);
- }
- return new CpuPixelBuffer(format, width, height);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
deleted file mode 100644
index e7e341b..0000000
--- a/libs/hwui/PixelBuffer.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_PIXEL_BUFFER_H
-#define ANDROID_HWUI_PIXEL_BUFFER_H
-
-#include <GLES3/gl3.h>
-
-#include <log/log.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Represents a pixel buffer. A pixel buffer will be backed either by a
- * PBO on OpenGL ES 3.0 and higher or by an array of uint8_t on other
- * versions. If the buffer is backed by a PBO it will of type
- * GL_PIXEL_UNPACK_BUFFER.
- *
- * To read from or write into a PixelBuffer you must first map the
- * buffer using the map(AccessMode) method. This method returns a
- * pointer to the beginning of the buffer.
- *
- * Before the buffer can be used by the GPU, for instance to upload
- * a texture, you must first unmap the buffer. To do so, call the
- * unmap() method.
- *
- * Mapping and unmapping a PixelBuffer can have the side effect of
- * changing the currently active GL_PIXEL_UNPACK_BUFFER. It is
- * therefore recommended to call Caches::unbindPixelbuffer() after
- * using a PixelBuffer to upload to a texture.
- */
-class PixelBuffer {
-public:
- enum BufferType { kBufferType_Auto, kBufferType_CPU };
-
- enum AccessMode {
- kAccessMode_None = 0,
- kAccessMode_Read = GL_MAP_READ_BIT,
- kAccessMode_Write = GL_MAP_WRITE_BIT,
- kAccessMode_ReadWrite = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT
- };
-
- /**
- * Creates a new PixelBuffer object with the specified format and
- * dimensions. The buffer is immediately allocated.
- *
- * The buffer type specifies how the buffer should be allocated.
- * By default this method will automatically choose whether to allocate
- * a CPU or GPU buffer.
- */
- static PixelBuffer* create(GLenum format, uint32_t width, uint32_t height,
- BufferType type = kBufferType_Auto);
-
- virtual ~PixelBuffer() {}
-
- /**
- * Returns the format of this render buffer.
- */
- GLenum getFormat() const { return mFormat; }
-
- /**
- * Maps this before with the specified access mode. This method
- * returns a pointer to the region of memory where the buffer was
- * mapped.
- *
- * If the buffer is already mapped when this method is invoked,
- * this method will return the previously mapped pointer. The
- * access mode can only be changed by calling unmap() first.
- *
- * The specified access mode cannot be kAccessMode_None.
- */
- virtual uint8_t* map(AccessMode mode = kAccessMode_ReadWrite) = 0;
-
- /**
- * Returns the current access mode for this buffer. If the buffer
- * is not mapped, this method returns kAccessMode_None.
- */
- AccessMode getAccessMode() const { return mAccessMode; }
-
- /**
- * Upload the specified rectangle of this pixel buffer as a
- * GL_TEXTURE_2D texture. Calling this method will trigger
- * an unmap() if necessary.
- */
- virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0;
-
- /**
- * Upload the specified rectangle of this pixel buffer as a
- * GL_TEXTURE_2D texture. Calling this method will trigger
- * an unmap() if necessary.
- *
- * This is a convenience function provided to save callers the
- * trouble of computing the offset parameter.
- */
- void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
- upload(x, y, width, height, getOffset(x, y));
- }
-
- /**
- * Returns the width of the render buffer in pixels.
- */
- uint32_t getWidth() const { return mWidth; }
-
- /**
- * Returns the height of the render buffer in pixels.
- */
- uint32_t getHeight() const { return mHeight; }
-
- /**
- * Returns the size of this pixel buffer in bytes.
- */
- uint32_t getSize() const { return mWidth * mHeight * formatSize(mFormat); }
-
- /**
- * Returns the offset of a pixel in this pixel buffer, in bytes.
- */
- uint32_t getOffset(uint32_t x, uint32_t y) const {
- return (y * mWidth + x) * formatSize(mFormat);
- }
-
- /**
- * Returns the number of bytes per pixel in the specified format.
- *
- * Supported formats:
- * GL_ALPHA
- * GL_RGBA
- */
- static uint32_t formatSize(GLenum format) {
- switch (format) {
- case GL_ALPHA:
- return 1;
- case GL_RGBA:
- return 4;
- }
- return 0;
- }
-
- /**
- * Returns the alpha channel offset in the specified format.
- *
- * Supported formats:
- * GL_ALPHA
- * GL_RGBA
- */
- static uint32_t formatAlphaOffset(GLenum format) {
- switch (format) {
- case GL_ALPHA:
- return 0;
- case GL_RGBA:
- return 3;
- }
-
- ALOGE("unsupported format: %d", format);
- return 0;
- }
-
-protected:
- /**
- * Creates a new render buffer in the specified format and dimensions.
- * The format must be GL_ALPHA or GL_RGBA.
- */
- PixelBuffer(GLenum format, uint32_t width, uint32_t height)
- : mFormat(format), mWidth(width), mHeight(height), mAccessMode(kAccessMode_None) {}
-
- /**
- * Unmaps this buffer, if needed. After the buffer is unmapped,
- * the pointer previously returned by map() becomes invalid and
- * should not be used.
- */
- virtual void unmap() = 0;
-
- GLenum mFormat;
-
- uint32_t mWidth;
- uint32_t mHeight;
-
- AccessMode mAccessMode;
-
-}; // class PixelBuffer
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_PIXEL_BUFFER_H
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
new file mode 100644
index 0000000..80f2b57
--- /dev/null
+++ b/libs/hwui/Readback.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+#include "Readback.h"
+
+#include "pipeline/skia/LayerDrawable.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/VulkanManager.h"
+
+#include <SkToSRGBColorFilter.h>
+#include <gui/Surface.h>
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+#include "DeferredLayerUpdater.h"
+#include "Properties.h"
+#include "hwui/Bitmap.h"
+#include "utils/Color.h"
+#include "utils/MathUtils.h"
+
+using namespace android::uirenderer::renderthread;
+
+namespace android {
+namespace uirenderer {
+
+CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
+ ATRACE_CALL();
+ // Setup the source
+ sp<GraphicBuffer> sourceBuffer;
+ sp<Fence> sourceFence;
+ Matrix4 texTransform;
+ status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data);
+ texTransform.invalidateType();
+ if (err != NO_ERROR) {
+ ALOGW("Failed to get last queued buffer, error = %d", err);
+ return CopyResult::UnknownError;
+ }
+ if (!sourceBuffer.get()) {
+ ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
+ return CopyResult::SourceEmpty;
+ }
+ if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
+ ALOGW("Surface is protected, unable to copy from it");
+ return CopyResult::SourceInvalid;
+ }
+ err = sourceFence->wait(500 /* ms */);
+ if (err != NO_ERROR) {
+ ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+ return CopyResult::Timeout;
+ }
+ if (!sourceBuffer.get()) {
+ return CopyResult::UnknownError;
+ }
+
+ sk_sp<SkColorSpace> colorSpace =
+ DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
+ sk_sp<SkColorFilter> colorSpaceFilter;
+ if (colorSpace && !colorSpace->isSRGB()) {
+ colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+ }
+ sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), kPremul_SkAlphaType);
+ return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap);
+}
+
+CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
+ LOG_ALWAYS_FATAL_IF(!hwBitmap->isHardware());
+
+ Rect srcRect;
+ Matrix4 transform;
+ transform.loadScale(1, -1, 1);
+ transform.translate(0, -1);
+
+ // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage".
+ // TODO: When this was attempted, it resulted in instability.
+ sk_sp<SkColorFilter> colorSpaceFilter;
+ sk_sp<SkColorSpace> colorSpace = hwBitmap->info().refColorSpace();
+ if (colorSpace && !colorSpace->isSRGB()) {
+ colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+ }
+ sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(hwBitmap->graphicBuffer()), kPremul_SkAlphaType);
+
+ // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format
+ // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for
+ // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test.
+ return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap);
+}
+
+CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
+ if (!mRenderThread.getGrContext()) {
+ return CopyResult::UnknownError;
+ }
+
+ // acquire most recent buffer for drawing
+ deferredLayer->updateTexImage();
+ deferredLayer->apply();
+ const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
+ CopyResult copyResult = CopyResult::UnknownError;
+ Layer* layer = deferredLayer->backingLayer();
+ if (layer) {
+ if (copyLayerInto(layer, nullptr, &dstRect, bitmap)) {
+ copyResult = CopyResult::Success;
+ }
+ }
+ return copyResult;
+}
+
+CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image,
+ sk_sp<SkColorFilter>& colorSpaceFilter, Matrix4& texTransform,
+ const Rect& srcRect, SkBitmap* bitmap) {
+ if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
+ mRenderThread.requireGlContext();
+ } else {
+ mRenderThread.vulkanManager().initialize();
+ }
+ if (!image.get()) {
+ return CopyResult::UnknownError;
+ }
+ int imgWidth = image->width();
+ int imgHeight = image->height();
+ sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
+
+ if (bitmap->colorType() == kRGBA_F16_SkColorType &&
+ !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) {
+ ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
+ return CopyResult::DestinationInvalid;
+ }
+
+ CopyResult copyResult = CopyResult::UnknownError;
+
+ int displayedWidth = imgWidth, displayedHeight = imgHeight;
+ // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
+ // size.
+ if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) {
+ std::swap(displayedWidth, displayedHeight);
+ }
+ SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
+ SkRect skiaSrcRect = srcRect.toSkRect();
+ if (skiaSrcRect.isEmpty()) {
+ skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
+ }
+ bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
+ if (!srcNotEmpty) {
+ return copyResult;
+ }
+
+ // See Readback::copyLayerInto for an overview of color space conversion.
+ // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder).
+ // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion.
+ // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally.
+ Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc);
+ bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
+ MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
+ layer.setForceFilter(!disableFilter);
+ layer.setSize(displayedWidth, displayedHeight);
+ texTransform.copyTo(layer.getTexTransform());
+ layer.setImage(image);
+ if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) {
+ copyResult = CopyResult::Success;
+ }
+
+ return copyResult;
+}
+
+bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+ SkBitmap* bitmap) {
+ /*
+ * In the past only TextureView readback was setting the temporary surface color space to null.
+ * Now all 3 readback flows are drawing into a SkSurface with null color space.
+ * At readback there are 3 options to convert the source image color space to the destination
+ * color space requested in "bitmap->info().colorSpace()":
+ * 1. Set color space for temporary surface render target to null (disables color management),
+ * colorspace tag from source SkImage is ignored by Skia,
+ * convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter,
+ * do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2",
+ * read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion.
+ *
+ * 2. Set color space for temporary surface render target to SRGB (not nullptr),
+ * colorspace tag on the source SkImage is used by Skia to enable conversion,
+ * convert SkImage to SRGB at draw time with drawImage (no filters),
+ * do a readback from temporary SkSurface, which will do a color conversion from SRGB to
+ * bitmap->info().colorSpace() on the CPU.
+ *
+ * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(),
+ * colorspace tag on the source SkImage is used by Skia to enable conversion,
+ * convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters),
+ * do a readback from SkSurface, which will not do any color conversion, because
+ * surface was created with the same color space as the "bitmap".
+ *
+ * Option 1 is used for all readback flows.
+ * Options 2 and 3 are new, because skia added support for non-SRGB render targets without
+ * linear blending.
+ * TODO: evaluate if options 2 or 3 for color space conversion are better.
+ */
+
+ // drop the colorSpace from the temporary surface.
+ SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
+
+ /* This intermediate surface is present to work around a bug in SwiftShader that
+ * prevents us from reading the contents of the layer's texture directly. The
+ * workaround involves first rendering that texture into an intermediate buffer and
+ * then reading from the intermediate buffer into the bitmap.
+ * Another reason to render in an offscreen buffer is to scale and to avoid an issue b/62262733
+ * with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
+ */
+ sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
+ SkBudgeted::kYes, surfaceInfo);
+
+ if (!tmpSurface.get()) {
+ surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
+ tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
+ surfaceInfo);
+ if (!tmpSurface.get()) {
+ ALOGW("Unable to readback GPU contents into the provided bitmap");
+ return false;
+ }
+ }
+
+ if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
+ tmpSurface->getCanvas(), layer, srcRect, dstRect,
+ false)) {
+ // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on
+ // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has
+ // disabled color conversion.
+ SkColorSpace* destColorSpace = bitmap->info().colorSpace();
+ SkBitmap tempSRGBBitmap;
+ SkBitmap tmpN32Bitmap;
+ SkBitmap* bitmapInSRGB;
+ if (destColorSpace && !destColorSpace->isSRGB()) {
+ tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB()));
+ bitmapInSRGB = &tempSRGBBitmap; // Need to convert latter from SRGB to non-SRGB.
+ } else {
+ bitmapInSRGB = bitmap; // No need for color conversion - write directly into output.
+ }
+ bool success = false;
+
+ // TODO: does any of the readbacks below clamp F16 exSRGB?
+ // Readback into a SRGB SkBitmap.
+ if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(),
+ bitmapInSRGB->rowBytes(), 0, 0)) {
+ success = true;
+ } else {
+ // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
+ // 8888 and then convert that into the destination format before giving up.
+ SkImageInfo bitmapInfo =
+ SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(),
+ SkColorSpace::MakeSRGB());
+ if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) &&
+ tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(),
+ tmpN32Bitmap.rowBytes(), 0, 0)) {
+ success = true;
+ bitmapInSRGB = &tmpN32Bitmap;
+ }
+ }
+
+ if (success) {
+ if (bitmapInSRGB != bitmap) {
+ // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to
+ // destination bitmap color format if needed.
+ if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(),
+ bitmap->rowBytes(), 0, 0)) {
+ return false;
+ }
+ }
+ bitmap->notifyPixelsChanged();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h
index ad3a8b6..d9e10ce 100644
--- a/libs/hwui/Readback.h
+++ b/libs/hwui/Readback.h
@@ -16,16 +16,21 @@
#pragma once
+#include "Matrix.h"
#include "Rect.h"
#include "renderthread/RenderThread.h"
#include <SkBitmap.h>
namespace android {
+class Bitmap;
class GraphicBuffer;
class Surface;
namespace uirenderer {
+class DeferredLayerUpdater;
+class Layer;
+
// Keep in sync with PixelCopy.java codes
enum class CopyResult {
Success = 0,
@@ -38,15 +43,22 @@
class Readback {
public:
+ explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
/**
* Copies the surface's most recently queued buffer into the provided bitmap.
*/
- virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) = 0;
- virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) = 0;
+ CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap);
-protected:
- explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {}
- virtual ~Readback() {}
+ CopyResult copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
+
+ CopyResult copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+
+private:
+ CopyResult copyImageInto(const sk_sp<SkImage>& image, sk_sp<SkColorFilter>& colorSpaceFilter,
+ Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap);
+
+ bool copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
+ SkBitmap* bitmap);
renderthread::RenderThread& mRenderThread;
};
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 0766e3b..7966845 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -16,7 +16,6 @@
#pragma once
-#include "Caches.h"
#include "DeviceInfo.h"
#include "Outline.h"
#include "Rect.h"
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 464a58d..65bee47 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -15,7 +15,6 @@
*/
#include "ResourceCache.h"
-#include "Caches.h"
namespace android {
@@ -112,13 +111,9 @@
ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : nullptr;
if (ref == nullptr) {
// If we're not tracking this resource, just delete it
- if (Caches::hasInstance()) {
- // DEAD CODE
- } else {
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- delete[](int8_t*) resource;
- }
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ delete[](int8_t*) resource;
return;
}
ref->destroyed = true;
@@ -135,14 +130,10 @@
if (ref->destroyed) {
switch (ref->resourceType) {
case kNinePatch: {
- if (Caches::hasInstance()) {
- // DEAD CODE
- } else {
- // A Res_png_9patch is actually an array of byte that's larger
- // than sizeof(Res_png_9patch). It must be freed as an array.
- int8_t* patch = (int8_t*)resource;
- delete[] patch;
- }
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ int8_t* patch = (int8_t*)resource;
+ delete[] patch;
} break;
}
}
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
deleted file mode 100644
index 1e90eeb..0000000
--- a/libs/hwui/Texture.cpp
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open 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 "Texture.h"
-#include "Caches.h"
-#include "utils/GLUtils.h"
-#include "utils/MathUtils.h"
-#include "utils/TraceUtils.h"
-
-#include <utils/Log.h>
-
-#include <math/mat4.h>
-
-#include <SkCanvas.h>
-
-namespace android {
-namespace uirenderer {
-
-// Number of bytes used by a texture in the given format
-static int bytesPerPixel(GLint glFormat) {
- switch (glFormat) {
- // The wrapped-texture case, usually means a SurfaceTexture
- case 0:
- return 0;
- case GL_LUMINANCE:
- case GL_ALPHA:
- return 1;
- case GL_SRGB8:
- case GL_RGB:
- return 3;
- case GL_SRGB8_ALPHA8:
- case GL_RGBA:
- return 4;
- case GL_RGBA16F:
- return 8;
- default:
- LOG_ALWAYS_FATAL("UNKNOWN FORMAT 0x%x", glFormat);
- }
-}
-
-void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) {
- if (force || wrapS != mWrapS || wrapT != mWrapT) {
- mWrapS = wrapS;
- mWrapT = wrapT;
-
- if (bindTexture) {
- mCaches.textureState().bindTexture(mTarget, mId);
- }
-
- glTexParameteri(mTarget, GL_TEXTURE_WRAP_S, wrapS);
- glTexParameteri(mTarget, GL_TEXTURE_WRAP_T, wrapT);
- }
-}
-
-void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force) {
- if (force || min != mMinFilter || mag != mMagFilter) {
- mMinFilter = min;
- mMagFilter = mag;
-
- if (bindTexture) {
- mCaches.textureState().bindTexture(mTarget, mId);
- }
-
- if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
-
- glTexParameteri(mTarget, GL_TEXTURE_MIN_FILTER, min);
- glTexParameteri(mTarget, GL_TEXTURE_MAG_FILTER, mag);
- }
-}
-
-void Texture::deleteTexture() {
- mCaches.textureState().deleteTexture(mId);
- mId = 0;
- mTarget = GL_NONE;
- if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
- EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
- eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
- mEglImageHandle = EGL_NO_IMAGE_KHR;
- }
-}
-
-bool Texture::updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format,
- GLenum target) {
- if (mWidth == width && mHeight == height && mFormat == format &&
- mInternalFormat == internalFormat && mTarget == target) {
- return false;
- }
- mWidth = width;
- mHeight = height;
- mFormat = format;
- mInternalFormat = internalFormat;
- mTarget = target;
- notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat));
- return true;
-}
-
-void Texture::resetCachedParams() {
- mWrapS = GL_REPEAT;
- mWrapT = GL_REPEAT;
- mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
- mMagFilter = GL_LINEAR;
-}
-
-void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format,
- GLenum type, const void* pixels) {
- GL_CHECKPOINT(MODERATE);
-
- // We don't have color space information, we assume the data is gamma encoded
- mIsLinear = false;
-
- bool needsAlloc = updateLayout(width, height, internalFormat, format, GL_TEXTURE_2D);
- if (!mId) {
- glGenTextures(1, &mId);
- needsAlloc = true;
- resetCachedParams();
- }
- mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
- if (needsAlloc) {
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels);
- } else if (pixels) {
- glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0, format, type, pixels);
- }
- GL_CHECKPOINT(MODERATE);
-}
-
-void Texture::uploadHardwareBitmapToTexture(GraphicBuffer* buffer) {
- EGLDisplay eglDisplayHandle = eglGetCurrentDisplay();
- if (mEglImageHandle != EGL_NO_IMAGE_KHR) {
- eglDestroyImageKHR(eglDisplayHandle, mEglImageHandle);
- mEglImageHandle = EGL_NO_IMAGE_KHR;
- }
- mEglImageHandle = eglCreateImageKHR(eglDisplayHandle, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
- buffer->getNativeBuffer(), 0);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, mEglImageHandle);
-}
-
-static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
- GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height,
- const GLvoid* data) {
- const bool useStride =
- stride != width && Caches::getInstance().extensions().hasUnpackRowLength();
- if ((stride == width) || useStride) {
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
- }
-
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
- }
-
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- }
- } else {
- // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
- // if the stride doesn't match the width
-
- GLvoid* temp = (GLvoid*)malloc(width * height * bpp);
- if (!temp) return;
-
- uint8_t* pDst = (uint8_t*)temp;
- uint8_t* pSrc = (uint8_t*)data;
- for (GLsizei i = 0; i < height; i++) {
- memcpy(pDst, pSrc, width * bpp);
- pDst += width * bpp;
- pSrc += stride * bpp;
- }
-
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp);
- } else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
- }
-
- free(temp);
- }
-}
-
-void Texture::colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType, bool needSRGB,
- GLint* outInternalFormat, GLint* outFormat,
- GLint* outType) {
- switch (colorType) {
- case kAlpha_8_SkColorType:
- *outFormat = GL_ALPHA;
- *outInternalFormat = GL_ALPHA;
- *outType = GL_UNSIGNED_BYTE;
- break;
- case kRGB_565_SkColorType:
- if (needSRGB) {
- // We would ideally use a GL_RGB/GL_SRGB8 texture but the
- // intermediate Skia bitmap needs to be ARGB_8888
- *outFormat = GL_RGBA;
- *outInternalFormat = caches.rgbaInternalFormat();
- *outType = GL_UNSIGNED_BYTE;
- } else {
- *outFormat = GL_RGB;
- *outInternalFormat = GL_RGB;
- *outType = GL_UNSIGNED_SHORT_5_6_5;
- }
- break;
- // ARGB_4444 is upconverted to RGBA_8888
- case kARGB_4444_SkColorType:
- case kN32_SkColorType:
- *outFormat = GL_RGBA;
- *outInternalFormat = caches.rgbaInternalFormat(needSRGB);
- *outType = GL_UNSIGNED_BYTE;
- break;
- case kGray_8_SkColorType:
- *outFormat = GL_LUMINANCE;
- *outInternalFormat = GL_LUMINANCE;
- *outType = GL_UNSIGNED_BYTE;
- break;
- case kRGBA_F16_SkColorType:
- if (caches.extensions().getMajorGlVersion() >= 3) {
- // This format is always linear
- *outFormat = GL_RGBA;
- *outInternalFormat = GL_RGBA16F;
- *outType = GL_HALF_FLOAT;
- } else {
- *outFormat = GL_RGBA;
- *outInternalFormat = caches.rgbaInternalFormat(true);
- *outType = GL_UNSIGNED_BYTE;
- }
- break;
- default:
- LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
- break;
- }
-}
-
-SkBitmap Texture::uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending,
- sk_sp<SkColorSpace> sRGB) {
- SkBitmap rgbaBitmap;
- rgbaBitmap.allocPixels(SkImageInfo::MakeN32(bitmap.width(), bitmap.height(),
- bitmap.info().alphaType(),
- hasLinearBlending ? sRGB : nullptr));
- rgbaBitmap.eraseColor(0);
-
- if (bitmap.colorType() == kRGBA_F16_SkColorType) {
- // Drawing RGBA_F16 onto ARGB_8888 is not supported
- bitmap.readPixels(rgbaBitmap.info().makeColorSpace(SkColorSpace::MakeSRGB()),
- rgbaBitmap.getPixels(), rgbaBitmap.rowBytes(), 0, 0);
- } else {
- SkCanvas canvas(rgbaBitmap);
- canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
- }
-
- return rgbaBitmap;
-}
-
-bool Texture::hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending) {
- return info.colorType() == kARGB_4444_SkColorType ||
- (info.colorType() == kRGB_565_SkColorType && hasLinearBlending &&
- info.colorSpace()->isSRGB()) ||
- (info.colorType() == kRGBA_F16_SkColorType &&
- Caches::getInstance().extensions().getMajorGlVersion() < 3);
-}
-
-void Texture::upload(Bitmap& bitmap) {
- ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
-
- // We could also enable mipmapping if both bitmap dimensions are powers
- // of 2 but we'd have to deal with size changes. Let's keep this simple
- const bool canMipMap = mCaches.extensions().hasNPot();
-
- // If the texture had mipmap enabled but not anymore,
- // force a glTexImage2D to discard the mipmap levels
- bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
- bool setDefaultParams = false;
-
- if (!mId) {
- glGenTextures(1, &mId);
- needsAlloc = true;
- setDefaultParams = true;
- }
-
- bool hasLinearBlending = mCaches.extensions().hasLinearBlending();
- bool needSRGB = transferFunctionCloseToSRGB(bitmap.info().colorSpace());
-
- GLint internalFormat, format, type;
- colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB && hasLinearBlending,
- &internalFormat, &format, &type);
-
- // Some devices don't support GL_RGBA16F, so we need to compare the color type
- // and internal GL format to decide what to do with 16 bit bitmaps
- bool rgba16fNeedsConversion =
- bitmap.colorType() == kRGBA_F16_SkColorType && internalFormat != GL_RGBA16F;
-
- // RGBA16F is always linear extended sRGB
- if (internalFormat == GL_RGBA16F) {
- mIsLinear = true;
- }
-
- mConnector.reset();
-
- // Alpha masks don't have color profiles
- // If an RGBA16F bitmap needs conversion, we know the target will be sRGB
- if (!mIsLinear && internalFormat != GL_ALPHA && !rgba16fNeedsConversion) {
- SkColorSpace* colorSpace = bitmap.info().colorSpace();
- // If the bitmap is sRGB we don't need conversion
- if (colorSpace != nullptr && !colorSpace->isSRGB()) {
- SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
- if (!colorSpace->toXYZD50(&xyzMatrix)) {
- ALOGW("Incompatible color space!");
- } else {
- SkColorSpaceTransferFn fn;
- if (!colorSpace->isNumericalTransferFn(&fn)) {
- ALOGW("Incompatible color space, no numerical transfer function!");
- } else {
- float data[16];
- xyzMatrix.asColMajorf(data);
-
- ColorSpace::TransferParameters p = {fn.fG, fn.fA, fn.fB, fn.fC,
- fn.fD, fn.fE, fn.fF};
- ColorSpace src("Unnamed", mat4f((const float*)&data[0]).upperLeft(), p);
- mConnector.reset(new ColorSpaceConnector(src, ColorSpace::sRGB()));
-
- // A non-sRGB color space might have a transfer function close enough to sRGB
- // that we can save shader instructions by using an sRGB sampler
- // This is only possible if we have hardware support for sRGB textures
- if (needSRGB && internalFormat == GL_RGBA && mCaches.extensions().hasSRGB() &&
- !bitmap.isHardware()) {
- internalFormat = GL_SRGB8_ALPHA8;
- }
- }
- }
- }
- }
-
- GLenum target = bitmap.isHardware() ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
- needsAlloc |= updateLayout(bitmap.width(), bitmap.height(), internalFormat, format, target);
-
- blend = !bitmap.isOpaque();
- mCaches.textureState().bindTexture(mTarget, mId);
-
- // TODO: Handle sRGB gray bitmaps
- if (CC_UNLIKELY(hasUnsupportedColorType(bitmap.info(), hasLinearBlending))) {
- SkBitmap skBitmap;
- bitmap.getSkBitmap(&skBitmap);
- sk_sp<SkColorSpace> sRGB = SkColorSpace::MakeSRGB();
- SkBitmap rgbaBitmap = uploadToN32(skBitmap, hasLinearBlending, std::move(sRGB));
- uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
- rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(), rgbaBitmap.height(),
- rgbaBitmap.getPixels());
- } else if (bitmap.isHardware()) {
- uploadHardwareBitmapToTexture(bitmap.graphicBuffer());
- } else {
- uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
- bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(),
- bitmap.pixels());
- }
-
- if (canMipMap) {
- mipMap = bitmap.hasHardwareMipMap();
- if (mipMap) {
- glGenerateMipmap(GL_TEXTURE_2D);
- }
- }
-
- if (setDefaultParams) {
- setFilter(GL_NEAREST);
- setWrap(GL_CLAMP_TO_EDGE);
- }
-}
-
-void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format,
- GLenum target) {
- mId = id;
- mWidth = width;
- mHeight = height;
- mFormat = format;
- mInternalFormat = internalFormat;
- mTarget = target;
- mConnector.reset();
- // We're wrapping an existing texture, so don't double count this memory
- notifySizeChanged(0);
-}
-
-TransferFunctionType Texture::getTransferFunctionType() const {
- if (mConnector.get() != nullptr && mInternalFormat != GL_SRGB8_ALPHA8) {
- const ColorSpace::TransferParameters& p = mConnector->getSource().getTransferParameters();
- if (MathUtils::isZero(p.e) && MathUtils::isZero(p.f)) {
- if (MathUtils::areEqual(p.a, 1.0f) && MathUtils::isZero(p.b) &&
- MathUtils::isZero(p.c) && MathUtils::isZero(p.d)) {
- if (MathUtils::areEqual(p.g, 1.0f)) {
- return TransferFunctionType::None;
- }
- return TransferFunctionType::Gamma;
- }
- return TransferFunctionType::Limited;
- }
- return TransferFunctionType::Full;
- }
- return TransferFunctionType::None;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
deleted file mode 100644
index 5b7e4e2..0000000
--- a/libs/hwui/Texture.h
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_HWUI_TEXTURE_H
-#define ANDROID_HWUI_TEXTURE_H
-
-#include "GpuMemoryTracker.h"
-#include "hwui/Bitmap.h"
-#include "utils/Color.h"
-
-#include <memory>
-
-#include <math/mat3.h>
-
-#include <ui/ColorSpace.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES2/gl2.h>
-#include <GLES3/gl3.h>
-#include <SkBitmap.h>
-
-namespace android {
-
-class GraphicBuffer;
-
-namespace uirenderer {
-
-class Caches;
-class UvMapper;
-class Layer;
-
-/**
- * Represents an OpenGL texture.
- */
-class Texture : public GpuMemoryTracker {
-public:
- static SkBitmap uploadToN32(const SkBitmap& bitmap, bool hasLinearBlending,
- sk_sp<SkColorSpace> sRGB);
- static bool hasUnsupportedColorType(const SkImageInfo& info, bool hasLinearBlending);
- static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
- bool needSRGB, GLint* outInternalFormat,
- GLint* outFormat, GLint* outType);
-
- explicit Texture(Caches& caches) : GpuMemoryTracker(GpuObjectType::Texture), mCaches(caches) {}
-
- virtual ~Texture() {}
-
- inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false) {
- setWrapST(wrap, wrap, bindTexture, force);
- }
-
- virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
- bool force = false);
-
- inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false) {
- setFilterMinMag(filter, filter, bindTexture, force);
- }
-
- virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
- bool force = false);
-
- /**
- * Convenience method to call glDeleteTextures() on this texture's id.
- */
- void deleteTexture();
-
- /**
- * Sets the width, height, and format of the texture along with allocating
- * the texture ID. Does nothing if the width, height, and format are already
- * the requested values.
- *
- * The image data is undefined after calling this.
- */
- void resize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) {
- upload(internalFormat, width, height, format,
- internalFormat == GL_RGBA16F ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE, nullptr);
- }
-
- /**
- * Updates this Texture with the contents of the provided Bitmap,
- * also setting the appropriate width, height, and format. It is not necessary
- * to call resize() prior to this.
- *
- * Note this does not set the generation from the Bitmap.
- */
- void upload(Bitmap& source);
-
- /**
- * Basically glTexImage2D/glTexSubImage2D.
- */
- void upload(GLint internalFormat, uint32_t width, uint32_t height, GLenum format, GLenum type,
- const void* pixels);
-
- /**
- * Wraps an existing texture.
- */
- void wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format,
- GLenum target);
-
- GLuint id() const { return mId; }
-
- uint32_t width() const { return mWidth; }
-
- uint32_t height() const { return mHeight; }
-
- GLint format() const { return mFormat; }
-
- GLint internalFormat() const { return mInternalFormat; }
-
- GLenum target() const { return mTarget; }
-
- /**
- * Returns nullptr if this texture does not require color space conversion
- * to sRGB, or a valid pointer to a ColorSpaceConnector if a conversion
- * is required.
- */
- constexpr const ColorSpaceConnector* getColorSpaceConnector() const { return mConnector.get(); }
-
- constexpr bool hasColorSpaceConversion() const { return mConnector.get() != nullptr; }
-
- TransferFunctionType getTransferFunctionType() const;
-
- /**
- * Returns true if this texture uses a linear encoding format.
- */
- constexpr bool isLinear() const { return mIsLinear; }
-
- /**
- * Generation of the backing bitmap,
- */
- uint32_t generation = 0;
- /**
- * Indicates whether the texture requires blending.
- */
- bool blend = false;
- /**
- * Indicates whether this texture should be cleaned up after use.
- */
- bool cleanup = false;
- /**
- * Optional, size of the original bitmap.
- */
- uint32_t bitmapSize = 0;
- /**
- * Indicates whether this texture will use trilinear filtering.
- */
- bool mipMap = false;
-
- /**
- * Optional, pointer to a texture coordinates mapper.
- */
- const UvMapper* uvMapper = nullptr;
-
- /**
- * Whether or not the Texture is marked in use and thus not evictable for
- * the current frame. This is reset at the start of a new frame.
- */
- void* isInUse = nullptr;
-
-private:
- // TODO: Temporarily grant private access to GlLayer, remove once
- // GlLayer can be de-tangled from being a dual-purpose render target
- // and external texture wrapper
- friend class GlLayer;
-
- // Returns true if the texture layout (size, format, etc.) changed, false if it was the same
- bool updateLayout(uint32_t width, uint32_t height, GLint internalFormat, GLint format,
- GLenum target);
- void uploadHardwareBitmapToTexture(GraphicBuffer* buffer);
- void resetCachedParams();
-
- GLuint mId = 0;
- uint32_t mWidth = 0;
- uint32_t mHeight = 0;
- GLint mFormat = 0;
- GLint mInternalFormat = 0;
- GLenum mTarget = GL_NONE;
- EGLImageKHR mEglImageHandle = EGL_NO_IMAGE_KHR;
-
- /* See GLES spec section 3.8.14
- * "In the initial state, the value assigned to TEXTURE_MIN_FILTER is
- * NEAREST_MIPMAP_LINEAR and the value for TEXTURE_MAG_FILTER is LINEAR.
- * s, t, and r wrap modes are all set to REPEAT."
- */
- GLenum mWrapS = GL_REPEAT;
- GLenum mWrapT = GL_REPEAT;
- GLenum mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
- GLenum mMagFilter = GL_LINEAR;
-
- // Indicates whether the content of the texture is in linear space
- bool mIsLinear = false;
-
- Caches& mCaches;
-
- std::unique_ptr<ColorSpaceConnector> mConnector;
-}; // struct Texture
-
-class AutoTexture {
-public:
- explicit AutoTexture(Texture* texture) : texture(texture) {}
- ~AutoTexture() {
- if (texture && texture->cleanup) {
- texture->deleteTexture();
- delete texture;
- }
- }
-
- Texture* const texture;
-}; // class AutoTexture
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TEXTURE_H
diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp
deleted file mode 100644
index 30fba7a..0000000
--- a/libs/hwui/VkLayer.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "VkLayer.h"
-
-#include "renderstate/RenderState.h"
-
-#include <SkCanvas.h>
-#include <SkSurface.h>
-
-namespace android {
-namespace uirenderer {
-
-void VkLayer::updateTexture() {
- sk_sp<SkSurface> surface;
- SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType);
- surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info);
- surface->getCanvas()->clear(SK_ColorBLUE);
- mImage = surface->makeImageSnapshot();
-}
-
-void VkLayer::onVkContextDestroyed() {
- mImage = nullptr;
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h
deleted file mode 100644
index e9664d0..0000000
--- a/libs/hwui/VkLayer.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "Layer.h"
-
-#include <SkImage.h>
-
-namespace android {
-namespace uirenderer {
-/**
- * A layer has dimensions and is backed by a VkImage.
- */
-class VkLayer : public Layer {
-public:
- VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
- : Layer(renderState, Api::Vulkan, colorFilter, alpha, mode)
- , mWidth(layerWidth)
- , mHeight(layerHeight)
- , mBlend(blend) {}
-
- virtual ~VkLayer() {}
-
- uint32_t getWidth() const override { return mWidth; }
-
- uint32_t getHeight() const override { return mHeight; }
-
- void setSize(uint32_t width, uint32_t height) override {
- mWidth = width;
- mHeight = height;
- }
-
- void setBlend(bool blend) override { mBlend = blend; }
-
- bool isBlend() const override { return mBlend; }
-
- sk_sp<SkImage> getImage() { return mImage; }
-
- void updateTexture();
-
- // If we've destroyed the vulkan context (VkInstance, VkDevice, etc.), we must make sure to
- // destroy any VkImages that were made with that context.
- void onVkContextDestroyed();
-
-private:
- int mWidth;
- int mHeight;
- bool mBlend;
-
- sk_sp<SkImage> mImage;
-
-}; // struct VkLayer
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index a7d37f8..440620a 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -15,11 +15,11 @@
*/
#include "Bitmap.h"
-#include "Caches.h"
#include "HardwareBitmapUploader.h"
#include "Properties.h"
#include "renderthread/RenderProxy.h"
#include "utils/Color.h"
+#include <utils/Trace.h>
#include <sys/mman.h>
@@ -290,7 +290,7 @@
if (isHardware()) {
outBitmap->allocPixels(SkImageInfo::Make(info().width(), info().height(),
info().colorType(), info().alphaType(), nullptr));
- uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
+ uirenderer::renderthread::RenderProxy::copyHWBitmapInto(this, outBitmap);
if (mInfo.colorSpace()) {
sk_sp<SkPixelRef> pixelRef = sk_ref_sp(outBitmap->pixelRef());
outBitmap->setInfo(mInfo);
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index d985633..e8332a8 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -178,8 +178,8 @@
struct stat st = {};
LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont);
void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
- std::unique_ptr<SkMemoryStream> fontData(new SkMemoryStream(data, st.st_size));
- sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(fontData.release());
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data, st.st_size));
+ sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData));
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont);
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index c41f6a6..3ca0f81 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -15,8 +15,6 @@
*/
#include "LayerDrawable.h"
-#include "GlLayer.h"
-#include "VkLayer.h"
#include "GrBackendSurface.h"
#include "SkColorFilter.h"
@@ -30,87 +28,78 @@
void LayerDrawable::onDraw(SkCanvas* canvas) {
Layer* layer = mLayerUpdater->backingLayer();
if (layer) {
- DrawLayer(canvas->getGrContext(), canvas, layer);
+ DrawLayer(canvas->getGrContext(), canvas, layer, nullptr, nullptr, true);
}
}
bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
- const SkRect* dstRect) {
+ const SkRect* srcRect, const SkRect* dstRect,
+ bool useLayerTransform) {
if (context == nullptr) {
SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
return false;
}
// transform the matrix based on the layer
- SkMatrix layerTransform;
- layer->getTransform().copyTo(layerTransform);
- sk_sp<SkImage> layerImage;
+ SkMatrix layerTransform = layer->getTransform();
+ sk_sp<SkImage> layerImage = layer->getImage();
const int layerWidth = layer->getWidth();
const int layerHeight = layer->getHeight();
- if (layer->getApi() == Layer::Api::OpenGL) {
- GlLayer* glLayer = static_cast<GlLayer*>(layer);
- GrGLTextureInfo externalTexture;
- externalTexture.fTarget = glLayer->getRenderTarget();
- externalTexture.fID = glLayer->getTextureId();
- // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't
- // expose that info we use it as our default. Further, given that we only use this texture
- // as a source this will not impact how Skia uses the texture. The only potential affect
- // this is anticipated to have is that for some format types if we are not bound as an OES
- // texture we may get invalid results for SKP capture if we read back the texture.
- externalTexture.fFormat = GL_RGBA8;
- GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture);
- layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
- kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
- } else {
- SkASSERT(layer->getApi() == Layer::Api::Vulkan);
- VkLayer* vkLayer = static_cast<VkLayer*>(layer);
- canvas->clear(SK_ColorGREEN);
- layerImage = vkLayer->getImage();
- }
if (layerImage) {
SkMatrix textureMatrixInv;
- layer->getTexTransform().copyTo(textureMatrixInv);
+ textureMatrixInv = layer->getTexTransform();
// TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
// use bottom left origin and remove flipV and invert transformations.
SkMatrix flipV;
flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
textureMatrixInv.preConcat(flipV);
textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight);
- textureMatrixInv.postScale(layerWidth, layerHeight);
+ textureMatrixInv.postScale(layerImage->width(), layerImage->height());
SkMatrix textureMatrix;
if (!textureMatrixInv.invert(&textureMatrix)) {
textureMatrix = textureMatrixInv;
}
SkMatrix matrix;
- if (dstRect) {
- // Destination rectangle is set only when we are trying to read back the content
- // of the layer. In this case we don't want to apply layer transform.
- matrix = textureMatrix;
- } else {
+ if (useLayerTransform) {
matrix = SkMatrix::Concat(layerTransform, textureMatrix);
+ } else {
+ matrix = textureMatrix;
}
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
paint.setColorFilter(layer->getColorSpaceWithFilter());
+ if (layer->getForceFilter()) {
+ paint.setFilterQuality(kLow_SkFilterQuality);
+ }
const bool nonIdentityMatrix = !matrix.isIdentity();
if (nonIdentityMatrix) {
canvas->save();
canvas->concat(matrix);
}
- if (dstRect) {
+ if (dstRect || srcRect) {
SkMatrix matrixInv;
if (!matrix.invert(&matrixInv)) {
matrixInv = matrix;
}
- SkRect srcRect = SkRect::MakeIWH(layerWidth, layerHeight);
- matrixInv.mapRect(&srcRect);
- SkRect skiaDestRect = *dstRect;
+ SkRect skiaSrcRect;
+ if (srcRect) {
+ skiaSrcRect = *srcRect;
+ } else {
+ skiaSrcRect = SkRect::MakeIWH(layerWidth, layerHeight);
+ }
+ matrixInv.mapRect(&skiaSrcRect);
+ SkRect skiaDestRect;
+ if (dstRect) {
+ skiaDestRect = *dstRect;
+ } else {
+ skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight);
+ }
matrixInv.mapRect(&skiaDestRect);
- canvas->drawImageRect(layerImage.get(), srcRect, skiaDestRect, &paint,
+ canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
SkCanvas::kFast_SrcRectConstraint);
} else {
canvas->drawImage(layerImage.get(), 0, 0, &paint);
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 18d1184..5c12590 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -33,7 +33,7 @@
explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
- const SkRect* dstRect = nullptr);
+ const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform);
protected:
virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 6700748..073b481 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -18,6 +18,8 @@
#include <algorithm>
#include <log/log.h>
#include <thread>
+#include <array>
+#include <openssl/sha.h>
#include "FileBlobCache.h"
#include "Properties.h"
#include "utils/TraceUtils.h"
@@ -41,7 +43,40 @@
return sCache;
}
-void ShaderCache::initShaderDiskCache() {
+bool ShaderCache::validateCache(const void* identity, ssize_t size) {
+ if (nullptr == identity && size == 0)
+ return true;
+
+ if (nullptr == identity || size < 0) {
+ if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
+ ALOGW("ShaderCache::validateCache invalid cache identity");
+ }
+ mBlobCache->clear();
+ return false;
+ }
+
+ SHA256_CTX ctx;
+ SHA256_Init(&ctx);
+
+ SHA256_Update(&ctx, identity, size);
+ mIDHash.resize(SHA256_DIGEST_LENGTH);
+ SHA256_Final(mIDHash.data(), &ctx);
+
+ std::array<uint8_t, SHA256_DIGEST_LENGTH> hash;
+ auto key = sIDKey;
+ auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size());
+
+ if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin()))
+ return true;
+
+ if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
+ ALOGW("ShaderCache::validateCache cache validation fails");
+ }
+ mBlobCache->clear();
+ return false;
+}
+
+void ShaderCache::initShaderDiskCache(const void* identity, ssize_t size) {
ATRACE_NAME("initShaderDiskCache");
std::lock_guard<std::mutex> lock(mMutex);
@@ -50,6 +85,7 @@
// desktop / laptop GPUs. Thus, disable the shader disk cache for emulator builds.
if (!Properties::runningInEmulator && mFilename.length() > 0) {
mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
+ validateCache(identity, size);
mInitialized = true;
}
}
@@ -104,6 +140,18 @@
return SkData::MakeFromMalloc(valueBuffer, valueSize);
}
+void ShaderCache::saveToDiskLocked() {
+ ATRACE_NAME("ShaderCache::saveToDiskLocked");
+ if (mInitialized && mBlobCache && mSavePending) {
+ if (mIDHash.size()) {
+ auto key = sIDKey;
+ mBlobCache->set(&key, sizeof(key), mIDHash.data(), mIDHash.size());
+ }
+ mBlobCache->writeToFile();
+ }
+ mSavePending = false;
+}
+
void ShaderCache::store(const SkData& key, const SkData& data) {
ATRACE_NAME("ShaderCache::store");
std::lock_guard<std::mutex> lock(mMutex);
@@ -129,11 +177,7 @@
std::thread deferredSaveThread([this]() {
sleep(mDeferredSaveDelay);
std::lock_guard<std::mutex> lock(mMutex);
- ATRACE_NAME("ShaderCache::saveToDisk");
- if (mInitialized && mBlobCache) {
- mBlobCache->writeToFile();
- }
- mSavePending = false;
+ saveToDiskLocked();
});
deferredSaveThread.detach();
}
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index 27473d6..82804cf 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -40,12 +40,21 @@
ANDROID_API static ShaderCache& get();
/**
- * "initShaderDiskCache" loads the serialized cache contents from disk and puts the ShaderCache
- * into an initialized state, such that it is able to insert and retrieve entries from the
- * cache. This should be called when HWUI pipeline is initialized. When not in the initialized
- * state the load and store methods will return without performing any cache operations.
+ * initShaderDiskCache" loads the serialized cache contents from disk,
+ * optionally checks that the on-disk cache matches a provided identity,
+ * and puts the ShaderCache into an initialized state, such that it is
+ * able to insert and retrieve entries from the cache. If identity is
+ * non-null and validation fails, the cache is initialized but contains
+ * no data. If size is less than zero, the cache is initilaized but
+ * contains no data.
+ *
+ * This should be called when HWUI pipeline is initialized. When not in
+ * the initialized state the load and store methods will return without
+ * performing any cache operations.
*/
- virtual void initShaderDiskCache();
+ virtual void initShaderDiskCache(const void *identity, ssize_t size);
+
+ virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); }
/**
* "setFilename" sets the name of the file that should be used to store
@@ -83,6 +92,19 @@
BlobCache* getBlobCacheLocked();
/**
+ * "validateCache" updates the cache to match the given identity. If the
+ * cache currently has the wrong identity, all entries in the cache are cleared.
+ */
+ bool validateCache(const void* identity, ssize_t size);
+
+ /**
+ * "saveToDiskLocked" attemps to save the current contents of the cache to
+ * disk. If the identity hash exists, we will insert the identity hash into
+ * the cache for next validation.
+ */
+ void saveToDiskLocked();
+
+ /**
* "mInitialized" indicates whether the ShaderCache is in the initialized
* state. It is initialized to false at construction time, and gets set to
* true when initialize is called.
@@ -111,6 +133,15 @@
std::string mFilename;
/**
+ * "mIDHash" is the current identity hash for the cache validation. It is
+ * initialized to an empty vector at construction time, and its content is
+ * generated in the call of the validateCache method. An empty vector
+ * indicates that cache validation is not performed, and the hash should
+ * not be stored on disk.
+ */
+ std::vector<uint8_t> mIDHash;
+
+ /**
* "mSavePending" indicates whether or not a deferred save operation is
* pending. Each time a key/value pair is inserted into the cache via
* load, a deferred save is initiated if one is not already pending.
@@ -140,6 +171,11 @@
*/
static ShaderCache sCache;
+ /**
+ * "sIDKey" is the cache key of the identity hash
+ */
+ static constexpr uint8_t sIDKey = 0;
+
friend class ShaderCacheTestUtils; //used for unit testing
};
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 78f5a71..d58b59e 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -17,7 +17,6 @@
#include "SkiaOpenGLPipeline.h"
#include "DeferredLayerUpdater.h"
-#include "GlLayer.h"
#include "LayerDrawable.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
@@ -128,77 +127,9 @@
return *requireSwap;
}
-bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
- if (!mRenderThread.getGrContext()) {
- return false;
- }
-
- // acquire most recent buffer for drawing
- deferredLayer->updateTexImage();
- deferredLayer->apply();
-
- // drop the colorSpace as we only support readback into sRGB or extended sRGB
- SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
-
- /* This intermediate surface is present to work around a bug in SwiftShader that
- * prevents us from reading the contents of the layer's texture directly. The
- * workaround involves first rendering that texture into an intermediate buffer and
- * then reading from the intermediate buffer into the bitmap.
- */
- sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
- SkBudgeted::kYes, surfaceInfo);
-
- if (!tmpSurface.get()) {
- surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
- tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
- surfaceInfo);
- if (!tmpSurface.get()) {
- ALOGW("Unable to readback GPU contents into the provided bitmap");
- return false;
- }
- }
-
- Layer* layer = deferredLayer->backingLayer();
- const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
- if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer,
- &dstRect)) {
- sk_sp<SkImage> tmpImage = tmpSurface->makeImageSnapshot();
- if (tmpImage->readPixels(surfaceInfo, bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
- bitmap->notifyPixelsChanged();
- return true;
- }
-
- // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into 8888
- // and then draw that into the destination format before giving up.
- SkBitmap tmpBitmap;
- SkImageInfo bitmapInfo =
- SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType());
- if (tmpBitmap.tryAllocPixels(bitmapInfo) &&
- tmpImage->readPixels(bitmapInfo, tmpBitmap.getPixels(), tmpBitmap.rowBytes(), 0, 0)) {
- SkCanvas canvas(*bitmap);
- SkPaint paint;
- paint.setBlendMode(SkBlendMode::kSrc);
- canvas.drawBitmap(tmpBitmap, 0, 0, &paint);
- bitmap->notifyPixelsChanged();
- return true;
- }
- }
-
- return false;
-}
-
-static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
- bool blend) {
- GlLayer* layer =
- new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
- layer->generateTexture();
- return layer;
-}
-
DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() {
mRenderThread.requireGlContext();
- return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::OpenGL);
+ return new DeferredLayerUpdater(mRenderThread.renderState());
}
void SkiaOpenGLPipeline::onStop() {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 2e2e152..808685a 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -40,7 +40,6 @@
FrameInfoVisualizer* profiler) override;
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
- bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
deleted file mode 100644
index f2f5056..0000000
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SkiaOpenGLReadback.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <GrBackendSurface.h>
-#include <SkCanvas.h>
-#include <SkSurface.h>
-#include <gl/GrGLInterface.h>
-#include <gl/GrGLTypes.h>
-#include "DeviceInfo.h"
-#include "Matrix.h"
-#include "Properties.h"
-#include "utils/MathUtils.h"
-
-using namespace android::uirenderer::renderthread;
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect,
- SkBitmap* bitmap) {
- GLuint sourceTexId;
- glGenTextures(1, &sourceTexId);
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
-
- sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface());
- LOG_ALWAYS_FATAL_IF(!glInterface.get());
- grContext = GrContext::MakeGL(std::move(glInterface));
- } else {
- grContext->resetContext();
- }
-
- if (bitmap->colorType() == kRGBA_F16_SkColorType &&
- !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) {
- ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
- return CopyResult::DestinationInvalid;
- }
-
- GrGLTextureInfo externalTexture;
- externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES;
- externalTexture.fID = sourceTexId;
- switch (bitmap->colorType()) {
- case kRGBA_F16_SkColorType:
- externalTexture.fFormat = GL_RGBA16F;
- break;
- case kN32_SkColorType:
- default:
- externalTexture.fFormat = GL_RGBA8;
- break;
- }
-
- GrBackendTexture backendTexture(imgWidth, imgHeight, GrMipMapped::kNo, externalTexture);
-
- CopyResult copyResult = CopyResult::UnknownError;
- sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), backendTexture,
- kTopLeft_GrSurfaceOrigin,
- bitmap->colorType()));
- if (image) {
- int displayedWidth = imgWidth, displayedHeight = imgHeight;
- // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
- // size.
- if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
- std::swap(displayedWidth, displayedHeight);
- }
- SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
- SkRect skiaSrcRect = srcRect.toSkRect();
- if (skiaSrcRect.isEmpty()) {
- skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
- }
- bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
-
- if (srcNotEmpty) {
- SkMatrix textureMatrixInv;
- imgTransform.copyTo(textureMatrixInv);
- // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
- // use bottom left origin and remove flipV and invert transformations.
- SkMatrix flipV;
- flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
- textureMatrixInv.preConcat(flipV);
- textureMatrixInv.preScale(1.0f / displayedWidth, 1.0f / displayedHeight);
- textureMatrixInv.postScale(imgWidth, imgHeight);
- SkMatrix textureMatrix;
- if (!textureMatrixInv.invert(&textureMatrix)) {
- textureMatrix = textureMatrixInv;
- }
-
- textureMatrixInv.mapRect(&skiaSrcRect);
- textureMatrixInv.mapRect(&skiaDestRect);
-
- // we render in an offscreen buffer to scale and to avoid an issue b/62262733
- // with reading incorrect data from EGLImage backed SkImage (likely a driver bug)
- sk_sp<SkSurface> scaledSurface =
- SkSurface::MakeRenderTarget(grContext.get(), SkBudgeted::kYes, bitmap->info());
- SkPaint paint;
- paint.setBlendMode(SkBlendMode::kSrc);
- // Apply a filter, which is matching OpenGL pipeline readback behaviour. Filter usage
- // is codified by tests using golden images like DecodeAccuracyTest.
- bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width())
- && MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
- if (!disableFilter) {
- paint.setFilterQuality(kLow_SkFilterQuality);
- }
- scaledSurface->getCanvas()->concat(textureMatrix);
- scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, skiaDestRect, &paint,
- SkCanvas::kFast_SrcRectConstraint);
-
- image = scaledSurface->makeImageSnapshot();
-
- if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
- bitmap->notifyPixelsChanged();
- copyResult = CopyResult::Success;
- }
- }
- }
-
- // make sure that we have deleted the texture (in the SkImage) before we
- // destroy the EGLImage that it was created from
- image.reset();
- glFinish();
-
- return copyResult;
-}
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
deleted file mode 100644
index 1ce4773..0000000
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include "EglReadback.h"
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-class SkiaOpenGLReadback : public EglReadback {
-public:
- SkiaOpenGLReadback(renderthread::RenderThread& thread) : EglReadback(thread) {}
-
-protected:
- virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform,
- int imgWidth, int imgHeight, const Rect& srcRect,
- SkBitmap* bitmap) override;
-};
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index b2519fe..611a34c 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -20,7 +20,6 @@
#include "Readback.h"
#include "SkiaPipeline.h"
#include "SkiaProfileRenderer.h"
-#include "VkLayer.h"
#include "renderstate/RenderState.h"
#include "renderthread/Frame.h"
@@ -109,21 +108,10 @@
return *requireSwap;
}
-bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
- // TODO: implement copyLayerInto for vulkan.
- return false;
-}
-
-static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
- bool blend) {
- return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
-}
-
DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
mVkManager.initialize();
- return new DeferredLayerUpdater(mRenderThread.renderState(), createLayer, Layer::Api::Vulkan);
+ return new DeferredLayerUpdater(mRenderThread.renderState());
}
void SkiaVulkanPipeline::onStop() {}
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 7806b42..900b054 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -38,7 +38,6 @@
FrameInfoVisualizer* profiler) override;
bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) override;
- bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
DeferredLayerUpdater* createTextureLayer() override;
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
renderthread::ColorMode colorMode) override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h b/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
deleted file mode 100644
index 65b89d6..0000000
--- a/libs/hwui/pipeline/skia/SkiaVulkanReadback.h
+++ /dev/null
@@ -1,44 +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.
- */
-
-#pragma once
-
-#include "Readback.h"
-
-namespace android {
-namespace uirenderer {
-namespace skiapipeline {
-
-class SkiaVulkanReadback : public Readback {
-public:
- SkiaVulkanReadback(renderthread::RenderThread& thread) : Readback(thread) {}
-
- virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect,
- SkBitmap* bitmap) override {
- //TODO: implement Vulkan readback.
- return CopyResult::UnknownError;
- }
-
- virtual CopyResult copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
- SkBitmap* bitmap) override {
- //TODO: implement Vulkan readback.
- return CopyResult::UnknownError;
- }
-};
-
-} /* namespace skiapipeline */
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp
deleted file mode 100644
index 3a6efb8..0000000
--- a/libs/hwui/renderstate/PixelBufferState.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "renderstate/PixelBufferState.h"
-
-namespace android {
-namespace uirenderer {
-
-PixelBufferState::PixelBufferState() : mCurrentPixelBuffer(0) {}
-
-bool PixelBufferState::bind(GLuint buffer) {
- if (mCurrentPixelBuffer != buffer) {
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
- mCurrentPixelBuffer = buffer;
- return true;
- }
- return false;
-}
-
-bool PixelBufferState::unbind() {
- if (mCurrentPixelBuffer) {
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
- mCurrentPixelBuffer = 0;
- return true;
- }
- return false;
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h
deleted file mode 100644
index f7ae6c5..0000000
--- a/libs/hwui/renderstate/PixelBufferState.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef RENDERSTATE_PIXELBUFFERSTATE_H
-#define RENDERSTATE_PIXELBUFFERSTATE_H
-
-#include <GLES3/gl3.h>
-
-namespace android {
-namespace uirenderer {
-
-class PixelBufferState {
- friend class Caches; // TODO: move to RenderState
-public:
- bool bind(GLuint buffer);
- bool unbind();
-
-private:
- PixelBufferState();
- GLuint mCurrentPixelBuffer;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif // RENDERSTATE_PIXELBUFFERSTATE_H
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 3be84f58..b524bcb 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -16,8 +16,6 @@
#include "renderstate/RenderState.h"
#include <GpuMemoryTracker.h>
#include "DeferredLayerUpdater.h"
-#include "GlLayer.h"
-#include "VkLayer.h"
#include "Snapshot.h"
#include "renderthread/CanvasContext.h"
@@ -39,44 +37,11 @@
RenderState::~RenderState() {
}
-void RenderState::onGLContextCreated() {
- GpuMemoryTracker::onGpuContextCreated();
-
- // This is delayed because the first access of Caches makes GL calls
- if (!mCaches) {
- mCaches = &Caches::createInstance(*this);
- }
- mCaches->init();
-}
-
-static void layerLostGlContext(Layer* layer) {
- LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL,
- "layerLostGlContext on non GL layer");
- static_cast<GlLayer*>(layer)->onGlContextLost();
-}
-
-void RenderState::onGLContextDestroyed() {
- // TODO: reset all cached state in state objects
- std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
-
- mCaches->terminate();
-
- destroyLayersInUpdater();
- GpuMemoryTracker::onGpuContextDestroyed();
-}
-
-void RenderState::onVkContextCreated() {
+void RenderState::onContextCreated() {
GpuMemoryTracker::onGpuContextCreated();
}
-static void layerDestroyedVkContext(Layer* layer) {
- LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::Vulkan,
- "layerLostVkContext on non Vulkan layer");
- static_cast<VkLayer*>(layer)->onVkContextDestroyed();
-}
-
-void RenderState::onVkContextDestroyed() {
- std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerDestroyedVkContext);
+void RenderState::onContextDestroyed() {
destroyLayersInUpdater();
GpuMemoryTracker::onGpuContextDestroyed();
}
@@ -85,10 +50,6 @@
return mRenderThread.getGrContext();
}
-void RenderState::flush(Caches::FlushMode mode) {
- if (mCaches) mCaches->flush(mode);
-}
-
void RenderState::onBitmapDestroyed(uint32_t pixelRefId) {
// DEAD CODE
}
@@ -126,42 +87,6 @@
glDeleteFramebuffers(1, &fbo);
}
-void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
- if (mode == DrawGlInfo::kModeProcessNoContext) {
- // If there's no context we don't need to interrupt as there's
- // no gl state to save/restore
- (*functor)(mode, info);
- } else {
- interruptForFunctorInvoke();
- (*functor)(mode, info);
- resumeFromFunctorInvoke();
- }
-}
-
-void RenderState::interruptForFunctorInvoke() {
- mCaches->textureState().resetActiveTexture();
- debugOverdraw(false, false);
- // TODO: We need a way to know whether the functor is sRGB aware (b/32072673)
- if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) {
- glDisable(GL_FRAMEBUFFER_SRGB_EXT);
- }
-}
-
-void RenderState::resumeFromFunctorInvoke() {
- if (mCaches->extensions().hasLinearBlending() && mCaches->extensions().hasSRGBWriteControl()) {
- glEnable(GL_FRAMEBUFFER_SRGB_EXT);
- }
-
- glViewport(0, 0, mViewportWidth, mViewportHeight);
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
- debugOverdraw(false, false);
-
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
- mCaches->textureState().activateTexture(0);
- mCaches->textureState().resetBoundTextures();
-}
-
void RenderState::debugOverdraw(bool enable, bool clear) {
// DEAD CODE
}
@@ -190,5 +115,9 @@
// DEAD CODE
}
+renderthread::RenderThread& RenderState::getRenderThread() {
+ return mRenderThread;
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 97785a4..f39aa4b 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -16,8 +16,6 @@
#ifndef RENDERSTATE_H
#define RENDERSTATE_H
-#include "Caches.h"
-#include "renderstate/PixelBufferState.h"
#include "utils/Macros.h"
#include <GLES2/gl2.h>
@@ -34,7 +32,6 @@
namespace android {
namespace uirenderer {
-class Caches;
class Layer;
class DeferredLayerUpdater;
@@ -44,22 +41,16 @@
class RenderThread;
}
-// TODO: Replace Cache's GL state tracking with this. For now it's more a thin
// wrapper of Caches for users to migrate to.
class RenderState {
PREVENT_COPY_AND_ASSIGN(RenderState);
friend class renderthread::RenderThread;
- friend class Caches;
friend class renderthread::CacheManager;
public:
- void onGLContextCreated();
- void onGLContextDestroyed();
+ void onContextCreated();
+ void onContextDestroyed();
- void onVkContextCreated();
- void onVkContextDestroyed();
-
- void flush(Caches::FlushMode flushMode);
void onBitmapDestroyed(uint32_t pixelRefId);
void setViewport(GLsizei width, GLsizei height);
@@ -70,8 +61,6 @@
GLuint createFramebuffer();
void deleteFramebuffer(GLuint fbo);
- void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
-
void debugOverdraw(bool enable, bool clear);
void registerLayer(Layer* layer) { mActiveLayers.insert(layer); }
@@ -101,16 +90,15 @@
void dump();
+ renderthread::RenderThread& getRenderThread();
+
private:
- void interruptForFunctorInvoke();
- void resumeFromFunctorInvoke();
void destroyLayersInUpdater();
explicit RenderState(renderthread::RenderThread& thread);
~RenderState();
renderthread::RenderThread& mRenderThread;
- Caches* mCaches = nullptr;
std::set<Layer*> mActiveLayers;
std::set<DeferredLayerUpdater*> mActiveLayerUpdaters;
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
deleted file mode 100644
index 470b4f5..0000000
--- a/libs/hwui/renderstate/TextureState.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "renderstate/TextureState.h"
-
-#include "Caches.h"
-#include "utils/TraceUtils.h"
-
-#include <GLES3/gl3.h>
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <memory>
-
-namespace android {
-namespace uirenderer {
-
-// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is
-static const int SHADOW_LUT_SIZE = 128;
-
-// Must define as many texture units as specified by kTextureUnitsCount
-const GLenum kTextureUnits[] = {GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, GL_TEXTURE3};
-
-TextureState::TextureState() : mTextureUnit(0) {
- glActiveTexture(kTextureUnits[0]);
- resetBoundTextures();
-
- GLint maxTextureUnits;
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
- LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount,
- "At least %d texture units are required!", kTextureUnitsCount);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-}
-
-TextureState::~TextureState() {
- if (mShadowLutTexture != nullptr) {
- mShadowLutTexture->deleteTexture();
- }
-}
-
-/**
- * Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
- * darkness at that spot. Input values of 0->1 should be mapped within the same
- * range, but can affect the curve for a different visual falloff.
- *
- * This is used to populate the shadow LUT texture for quick lookup in the
- * shadow shader.
- */
-static float computeShadowOpacity(float ratio) {
- // exponential falloff function provided by UX
- float val = 1 - ratio;
- return exp(-val * val * 4.0) - 0.018;
-}
-
-void TextureState::constructTexture(Caches& caches) {
- if (mShadowLutTexture == nullptr) {
- mShadowLutTexture.reset(new Texture(caches));
-
- unsigned char bytes[SHADOW_LUT_SIZE];
- for (int i = 0; i < SHADOW_LUT_SIZE; i++) {
- float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f);
- bytes[i] = computeShadowOpacity(inputRatio) * 255;
- }
- mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes);
- mShadowLutTexture->setFilter(GL_LINEAR);
- mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE);
- }
-}
-
-void TextureState::activateTexture(GLuint textureUnit) {
- LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount,
- "Tried to use texture unit index %d, only %d exist", textureUnit,
- kTextureUnitsCount);
- if (mTextureUnit != textureUnit) {
- glActiveTexture(kTextureUnits[textureUnit]);
- mTextureUnit = textureUnit;
- }
-}
-
-void TextureState::resetActiveTexture() {
- mTextureUnit = -1;
-}
-
-void TextureState::bindTexture(GLuint texture) {
- if (mBoundTextures[mTextureUnit] != texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
- mBoundTextures[mTextureUnit] = texture;
- }
-}
-
-void TextureState::bindTexture(GLenum target, GLuint texture) {
- if (target == GL_TEXTURE_2D) {
- bindTexture(texture);
- } else {
- // GLConsumer directly calls glBindTexture() with
- // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
- // since the cached state could be stale
- glBindTexture(target, texture);
- }
-}
-
-void TextureState::deleteTexture(GLuint texture) {
- // When glDeleteTextures() is called on a currently bound texture,
- // OpenGL ES specifies that the texture is then considered unbound
- // Consider the following series of calls:
- //
- // glGenTextures -> creates texture name 2
- // glBindTexture(2)
- // glDeleteTextures(2) -> 2 is now unbound
- // glGenTextures -> can return 2 again
- //
- // If we don't call glBindTexture(2) after the second glGenTextures
- // call, any texture operation will be performed on the default
- // texture (name=0)
-
- unbindTexture(texture);
-
- glDeleteTextures(1, &texture);
-}
-
-void TextureState::resetBoundTextures() {
- for (int i = 0; i < kTextureUnitsCount; i++) {
- mBoundTextures[i] = 0;
- }
-}
-
-void TextureState::unbindTexture(GLuint texture) {
- for (int i = 0; i < kTextureUnitsCount; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
-}
-
-} /* namespace uirenderer */
-} /* namespace android */
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
deleted file mode 100644
index f1996d4..0000000
--- a/libs/hwui/renderstate/TextureState.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef RENDERSTATE_TEXTURESTATE_H
-#define RENDERSTATE_TEXTURESTATE_H
-
-#include "Texture.h"
-#include "Vertex.h"
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-#include <memory>
-
-namespace android {
-namespace uirenderer {
-
-class Texture;
-
-class TextureState {
- friend class Caches; // TODO: move to RenderState
-public:
- void constructTexture(Caches& caches);
-
- /**
- * Activate the specified texture unit. The texture unit must
- * be specified using an integer number (0 for GL_TEXTURE0 etc.)
- */
- void activateTexture(GLuint textureUnit);
-
- /**
- * Invalidate the cached value of the active texture unit.
- */
- void resetActiveTexture();
-
- /**
- * Binds the specified texture as a GL_TEXTURE_2D texture.
- * All texture bindings must be performed with this method or
- * bindTexture(GLenum, GLuint).
- */
- void bindTexture(GLuint texture);
-
- /**
- * Binds the specified texture with the specified render target.
- * All texture bindings must be performed with this method or
- * bindTexture(GLuint).
- */
- void bindTexture(GLenum target, GLuint texture);
-
- /**
- * Deletes the specified texture and clears it from the cache
- * of bound textures.
- * All textures must be deleted using this method.
- */
- void deleteTexture(GLuint texture);
-
- /**
- * Signals that the cache of bound textures should be cleared.
- * Other users of the context may have altered which textures are bound.
- */
- void resetBoundTextures();
-
- /**
- * Clear the cache of bound textures.
- */
- void unbindTexture(GLuint texture);
-
- Texture* getShadowLutTexture() { return mShadowLutTexture.get(); }
-
-private:
- // total number of texture units available for use
- static const int kTextureUnitsCount = 4;
-
- TextureState();
- ~TextureState();
- GLuint mTextureUnit;
-
- // Caches texture bindings for the GL_TEXTURE_2D target
- GLuint mBoundTextures[kTextureUnitsCount];
-
- std::unique_ptr<Texture> mShadowLutTexture;
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index bec80b1e..7acc44c 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -21,6 +21,7 @@
#include "RenderThread.h"
#include "pipeline/skia/ShaderCache.h"
#include "pipeline/skia/SkiaMemoryTracer.h"
+#include "Properties.h"
#include "renderstate/RenderState.h"
#include <GrContextOptions.h>
@@ -50,7 +51,6 @@
mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(
mMaxSurfaceArea / 2,
skiapipeline::VectorDrawableAtlas::StorageMode::disallowSharedSurface);
- skiapipeline::ShaderCache::get().initShaderDiskCache();
}
void CacheManager::reset(sk_sp<GrContext> context) {
@@ -103,7 +103,7 @@
}
};
-void CacheManager::configureContext(GrContextOptions* contextOptions) {
+void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) {
contextOptions->fAllowPathMaskCaching = true;
float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f;
@@ -133,7 +133,9 @@
contextOptions->fExecutor = mTaskProcessor.get();
}
- contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
+ auto& cache = skiapipeline::ShaderCache::get();
+ cache.initShaderDiskCache(identity, size);
+ contextOptions->fPersistentCache = &cache;
contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
@@ -214,11 +216,12 @@
log.appendFormat(" Layer Info:\n");
}
+ const char* layerType = Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL
+ ? "GlLayer" : "VkLayer";
size_t layerMemoryTotal = 0;
for (std::set<Layer*>::iterator it = renderState->mActiveLayers.begin();
it != renderState->mActiveLayers.end(); it++) {
const Layer* layer = *it;
- const char* layerType = layer->getApi() == Layer::Api::OpenGL ? "GlLayer" : "VkLayer";
log.appendFormat(" %s size %dx%d\n", layerType, layer->getWidth(),
layer->getHeight());
layerMemoryTotal += layer->getWidth() * layer->getHeight() * 4;
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 7d73352..35fc91a 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -44,7 +44,7 @@
public:
enum class TrimMemoryMode { Complete, UiHidden };
- void configureContext(GrContextOptions* context);
+ void configureContext(GrContextOptions* context, const void* identity, ssize_t size);
void trimMemory(TrimMemoryMode mode);
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5d72523..727cef3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -18,7 +18,6 @@
#include <GpuMemoryTracker.h>
#include "AnimationContext.h"
-#include "Caches.h"
#include "EglManager.h"
#include "Frame.h"
#include "LayerUpdateQueue.h"
@@ -495,13 +494,6 @@
}
GpuMemoryTracker::onFrameCompleted();
-#ifdef BUGREPORT_FONT_CACHE_USAGE
- auto renderType = Properties::getRenderPipelineType();
- if (RenderPipelineType::OpenGL == renderType) {
- Caches& caches = Caches::getInstance();
- caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted();
- }
-#endif
}
// Called by choreographer to do an RT-driven animation
@@ -570,10 +562,6 @@
mPrefetchedLayers.insert(node);
}
-bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
- return mRenderPipeline->copyLayerInto(layer, bitmap);
-}
-
void CanvasContext::destroyHardwareResources() {
stopDrawing();
if (mRenderPipeline->isContextReady()) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 8ca54af..02ee72f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -135,7 +135,6 @@
void prepareAndDraw(RenderNode* node);
void buildLayer(RenderNode* node);
- bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
void markLayerInUse(RenderNode* node);
void destroyHardwareResources();
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index cd21822..5f8d7ad 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -18,6 +18,7 @@
#include <cutils/properties.h>
#include <log/log.h>
+#include <private/gui/SyncFeatures.h>
#include <utils/Trace.h>
#include "utils/StringUtils.h"
@@ -464,6 +465,109 @@
return preserved;
}
+status_t EglManager::fenceWait(sp<Fence>& fence) {
+ if (!hasEglContext()) {
+ ALOGE("EglManager::fenceWait: EGLDisplay not initialized");
+ return INVALID_OPERATION;
+ }
+
+ if (SyncFeatures::getInstance().useWaitSync() &&
+ SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Block GPU on the fence.
+ // Create an EGLSyncKHR from the current fence.
+ int fenceFd = fence->dup();
+ if (fenceFd == -1) {
+ ALOGE("EglManager::fenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+ EGLint attribs[] = {
+ EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd,
+ EGL_NONE
+ };
+ EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay,
+ EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ close(fenceFd);
+ ALOGE("EglManager::fenceWait: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should
+ // return an EGLint or void. Ignore the return value for now, as
+ // it's not strictly needed.
+ eglWaitSyncKHR(mEglDisplay, sync, 0);
+ EGLint eglErr = eglGetError();
+ eglDestroySyncKHR(mEglDisplay, sync);
+ if (eglErr != EGL_SUCCESS) {
+ ALOGE("EglManager::fenceWait: error waiting for EGL fence: %#x", eglErr);
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ // Block CPU on the fence.
+ status_t err = fence->waitForever("EglManager::fenceWait");
+ if (err != NO_ERROR) {
+ ALOGE("EglManager::fenceWait: error waiting for fence: %d", err);
+ return err;
+ }
+ }
+ return OK;
+}
+
+status_t EglManager::createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence,
+ sp<Fence>& nativeFence) {
+ if (!hasEglContext()) {
+ ALOGE("EglManager::createReleaseFence: EGLDisplay not initialized");
+ return INVALID_OPERATION;
+ }
+
+ if (SyncFeatures::getInstance().useNativeFenceSync()) {
+ EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay,
+ EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ int fenceFd = eglDupNativeFenceFDANDROID(mEglDisplay, sync);
+ eglDestroySyncKHR(mEglDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGE("EglManager::createReleaseFence: error dup'ing native fence "
+ "fd: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ nativeFence = new Fence(fenceFd);
+ *eglFence = EGL_NO_SYNC_KHR;
+ } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+ if (*eglFence != EGL_NO_SYNC_KHR) {
+ // There is already a fence for the current slot. We need to
+ // wait on that before replacing it with another fence to
+ // ensure that all outstanding buffer accesses have completed
+ // before the producer accesses it.
+ EGLint result = eglClientWaitSyncKHR(mEglDisplay, *eglFence, 0, 1000000000);
+ if (result == EGL_FALSE) {
+ ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence");
+ return TIMED_OUT;
+ }
+ eglDestroySyncKHR(mEglDisplay, *eglFence);
+ }
+
+ // Create a fence for the outstanding accesses in the current
+ // OpenGL ES context.
+ *eglFence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+ if (*eglFence == EGL_NO_SYNC_KHR) {
+ ALOGE("EglManager::createReleaseFence: error creating fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ }
+ return OK;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 8e8bb8b..507673a 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -17,8 +17,10 @@
#define EGLMANAGER_H
#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include <SkRect.h>
#include <cutils/compiler.h>
+#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
@@ -66,6 +68,14 @@
EGLDisplay eglDisplay() const { return mEglDisplay; }
+ // Inserts a wait on fence command into the OpenGL ES command stream. If EGL extension
+ // support is missing, block the CPU on the fence.
+ status_t fenceWait(sp<Fence>& fence);
+
+ // Creates a fence that is signaled, when all the pending GL commands are flushed.
+ // Depending on installed extensions, the result is either Android native fence or EGL fence.
+ status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence);
+
private:
void initExtensions();
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index b94a758..b7b7853 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -60,7 +60,6 @@
FrameInfoVisualizer* profiler) = 0;
virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
- virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0;
virtual DeferredLayerUpdater* createTextureLayer() = 0;
virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
virtual void onStop() = 0;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index e3807e6..7a5348a 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -160,8 +160,10 @@
}
bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) {
- return mRenderThread.queue().runSync(
- [&]() -> bool { return mContext->copyLayerInto(layer, &bitmap); });
+ auto& thread = RenderThread::getInstance();
+ return thread.queue().runSync(
+ [&]() -> bool { return thread.readback().copyLayerInto(layer, &bitmap)
+ == CopyResult::Success; });
}
void RenderProxy::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -331,14 +333,14 @@
}
}
-int RenderProxy::copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap) {
+int RenderProxy::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
RenderThread& thread = RenderThread::getInstance();
if (gettid() == thread.getTid()) {
// TODO: fix everything that hits this. We should never be triggering a readback ourselves.
- return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap);
+ return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
} else {
return thread.queue().runSync([&]() -> int {
- return (int)thread.readback().copyGraphicBufferInto(buffer, bitmap);
+ return (int)thread.readback().copyHWBitmapInto(hwBitmap, bitmap);
});
}
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index c2964a4..969ad00 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -125,7 +125,7 @@
int bottom, SkBitmap* bitmap);
ANDROID_API static void prepareToDraw(Bitmap& bitmap);
- static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap);
+ static int copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap);
static void onBitmapDestroyed(uint32_t pixelRefId);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index c1284ec..7258a0a 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -19,13 +19,12 @@
#include "CanvasContext.h"
#include "DeviceInfo.h"
#include "EglManager.h"
+#include "Readback.h"
#include "RenderProxy.h"
#include "VulkanManager.h"
#include "hwui/Bitmap.h"
#include "pipeline/skia/SkiaOpenGLPipeline.h"
-#include "pipeline/skia/SkiaOpenGLReadback.h"
#include "pipeline/skia/SkiaVulkanPipeline.h"
-#include "pipeline/skia/SkiaVulkanReadback.h"
#include "renderstate/RenderState.h"
#include "utils/FatVector.h"
#include "utils/TimeUtils.h"
@@ -178,7 +177,7 @@
return;
}
mEglManager->initialize();
- renderState().onGLContextCreated();
+ renderState().onContextCreated();
#ifdef HWUI_GLES_WRAP_ENABLED
debug::GlesDriver* driver = debug::GlesDriver::get();
@@ -191,7 +190,9 @@
GrContextOptions options;
options.fPreferExternalImagesOverES3 = true;
options.fDisableDistanceFieldPaths = true;
- cacheManager().configureContext(&options);
+ auto glesVersion = reinterpret_cast<const char*>(glGetString(GL_VERSION));
+ auto size = glesVersion ? strlen(glesVersion) : -1;
+ cacheManager().configureContext(&options, glesVersion, size);
sk_sp<GrContext> grContext(GrContext::MakeGL(std::move(glInterface), options));
LOG_ALWAYS_FATAL_IF(!grContext.get());
setGrContext(grContext);
@@ -200,7 +201,7 @@
void RenderThread::destroyGlContext() {
if (mEglManager->hasEglContext()) {
setGrContext(nullptr);
- renderState().onGLContextDestroyed();
+ renderState().onContextDestroyed();
mEglManager->destroy();
}
}
@@ -233,18 +234,7 @@
Readback& RenderThread::readback() {
if (!mReadback) {
- auto renderType = Properties::getRenderPipelineType();
- switch (renderType) {
- case RenderPipelineType::SkiaGL:
- mReadback = new skiapipeline::SkiaOpenGLReadback(*this);
- break;
- case RenderPipelineType::SkiaVulkan:
- mReadback = new skiapipeline::SkiaVulkanReadback(*this);
- break;
- default:
- LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
- break;
- }
+ mReadback = new Readback(*this);
}
return *mReadback;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 1517f57..038e13c 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -40,7 +40,7 @@
VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {}
void VulkanManager::destroy() {
- mRenderThread.renderState().onVkContextDestroyed();
+ mRenderThread.renderState().onContextDestroyed();
mRenderThread.setGrContext(nullptr);
if (VK_NULL_HANDLE != mCommandPool) {
@@ -391,7 +391,8 @@
GrContextOptions options;
options.fDisableDistanceFieldPaths = true;
- mRenderThread.cacheManager().configureContext(&options);
+ // TODO: get a string describing the SPIR-V compiler version and use it here
+ mRenderThread.cacheManager().configureContext(&options, nullptr, 0);
sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options));
LOG_ALWAYS_FATAL_IF(!grContext.get());
mRenderThread.setGrContext(grContext);
@@ -404,7 +405,7 @@
mSwapBehavior = SwapBehavior::BufferAge;
}
- mRenderThread.renderState().onVkContextCreated();
+ mRenderThread.renderState().onContextCreated();
}
// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
@@ -981,6 +982,22 @@
return surface->mCurrentTime - lastUsed;
}
+status_t VulkanManager::fenceWait(sp<Fence>& fence) {
+ //TODO: Insert a wait on fence command into the Vulkan command buffer.
+ // Block CPU on the fence.
+ status_t err = fence->waitForever("VulkanManager::fenceWait");
+ if (err != NO_ERROR) {
+ ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err);
+ return err;
+ }
+ return OK;
+}
+
+status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) {
+ //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed.
+ return OK;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 5524c39..ebc11a5 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -23,6 +23,8 @@
#include <vulkan/vulkan.h>
#include <SkSurface.h>
+#include <ui/Fence.h>
+#include <utils/StrongPointer.h>
#include <vk/GrVkBackendContext.h>
class GrVkExtensions;
@@ -110,6 +112,12 @@
// Presents the current VkImage.
void swapBuffers(VulkanSurface* surface);
+ // Inserts a wait on fence command into the Vulkan command buffer.
+ status_t fenceWait(sp<Fence>& fence);
+
+ // Creates a fence that is signaled, when all the pending Vulkan commands are flushed.
+ status_t createReleaseFence(sp<Fence>& nativeFence);
+
private:
friend class RenderThread;
diff --git a/libs/hwui/surfacetexture/EGLConsumer.cpp b/libs/hwui/surfacetexture/EGLConsumer.cpp
new file mode 100644
index 0000000..c8220c6
--- /dev/null
+++ b/libs/hwui/surfacetexture/EGLConsumer.cpp
@@ -0,0 +1,675 @@
+/*
+ * 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.
+ */
+
+#include <inttypes.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferQueue.h>
+#include <private/gui/SyncFeatures.h>
+#include "EGLConsumer.h"
+#include "SurfaceTexture.h"
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+
+#define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content"
+#define EGL_PROTECTED_CONTENT_EXT 0x32C0
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define EGC_LOGV(x, ...) ALOGV("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGD(x, ...) ALOGD("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGW(x, ...) ALOGW("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+#define EGC_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+static const struct {
+ uint32_t width, height;
+ char const* bits;
+} kDebugData = {15, 12,
+ "_______________"
+ "_______________"
+ "_____XX_XX_____"
+ "__X_X_____X_X__"
+ "__X_XXXXXXX_X__"
+ "__XXXXXXXXXXX__"
+ "___XX_XXX_XX___"
+ "____XXXXXXX____"
+ "_____X___X_____"
+ "____X_____X____"
+ "_______________"
+ "_______________"};
+
+Mutex EGLConsumer::sStaticInitLock;
+sp<GraphicBuffer> EGLConsumer::sReleasedTexImageBuffer;
+
+static bool hasEglProtectedContentImpl() {
+ EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ const char* exts = eglQueryString(dpy, EGL_EXTENSIONS);
+ size_t cropExtLen = strlen(PROT_CONTENT_EXT_STR);
+ size_t extsLen = strlen(exts);
+ bool equal = !strcmp(PROT_CONTENT_EXT_STR, exts);
+ bool atStart = !strncmp(PROT_CONTENT_EXT_STR " ", exts, cropExtLen + 1);
+ bool atEnd = (cropExtLen + 1) < extsLen &&
+ !strcmp(" " PROT_CONTENT_EXT_STR, exts + extsLen - (cropExtLen + 1));
+ bool inMiddle = strstr(exts, " " PROT_CONTENT_EXT_STR " ");
+ return equal || atStart || atEnd || inMiddle;
+}
+
+static bool hasEglProtectedContent() {
+ // Only compute whether the extension is present once the first time this
+ // function is called.
+ static bool hasIt = hasEglProtectedContentImpl();
+ return hasIt;
+}
+
+EGLConsumer::EGLConsumer() : mEglDisplay(EGL_NO_DISPLAY), mEglContext(EGL_NO_CONTEXT) {}
+
+status_t EGLConsumer::updateTexImage(SurfaceTexture& st) {
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = checkAndUpdateEglStateLocked(st);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ BufferItem item;
+
+ // Acquire the next buffer.
+ // In asynchronous mode the list is guaranteed to be one buffer
+ // deep, while in synchronous mode we use the oldest buffer.
+ err = st.acquireBufferLocked(&item, 0);
+ if (err != NO_ERROR) {
+ if (err == BufferQueue::NO_BUFFER_AVAILABLE) {
+ // We always bind the texture even if we don't update its contents.
+ EGC_LOGV("updateTexImage: no buffers were available");
+ glBindTexture(st.mTexTarget, st.mTexName);
+ err = NO_ERROR;
+ } else {
+ EGC_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err);
+ }
+ return err;
+ }
+
+ // Release the previous buffer.
+ err = updateAndReleaseLocked(item, nullptr, st);
+ if (err != NO_ERROR) {
+ // We always bind the texture.
+ glBindTexture(st.mTexTarget, st.mTexName);
+ return err;
+ }
+
+ // Bind the new buffer to the GL texture, and wait until it's ready.
+ return bindTextureImageLocked(st);
+}
+
+status_t EGLConsumer::releaseTexImage(SurfaceTexture& st) {
+ // Make sure the EGL state is the same as in previous calls.
+ status_t err = NO_ERROR;
+
+ // if we're detached, no need to validate EGL's state -- we won't use it.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ err = checkAndUpdateEglStateLocked(st, true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ // Update the EGLConsumer state.
+ int buf = st.mCurrentTexture;
+ if (buf != BufferQueue::INVALID_BUFFER_SLOT) {
+ EGC_LOGV("releaseTexImage: (slot=%d, mOpMode=%d)", buf, (int)st.mOpMode);
+
+ // if we're detached, we just use the fence that was created in detachFromContext()
+ // so... basically, nothing more to do here.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ // Do whatever sync ops we need to do before releasing the slot.
+ err = syncForReleaseLocked(mEglDisplay, st);
+ if (err != NO_ERROR) {
+ EGC_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
+ return err;
+ }
+ }
+
+ err = st.releaseBufferLocked(buf, st.mSlots[buf].mGraphicBuffer, mEglDisplay,
+ EGL_NO_SYNC_KHR);
+ if (err < NO_ERROR) {
+ EGC_LOGE("releaseTexImage: failed to release buffer: %s (%d)", strerror(-err), err);
+ return err;
+ }
+
+ if (mReleasedTexImage == nullptr) {
+ mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+ }
+
+ st.mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ mCurrentTextureImage = mReleasedTexImage;
+ st.mCurrentCrop.makeInvalid();
+ st.mCurrentTransform = 0;
+ st.mCurrentTimestamp = 0;
+ st.mCurrentDataSpace = HAL_DATASPACE_UNKNOWN;
+ st.mCurrentFence = Fence::NO_FENCE;
+ st.mCurrentFenceTime = FenceTime::NO_FENCE;
+
+ // detached, don't touch the texture (and we may not even have an
+ // EGLDisplay here.
+ if (st.mOpMode == SurfaceTexture::OpMode::attachedToGL) {
+ // This binds a dummy buffer (mReleasedTexImage).
+ status_t result = bindTextureImageLocked(st);
+ if (result != NO_ERROR) {
+ return result;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+sp<GraphicBuffer> EGLConsumer::getDebugTexImageBuffer() {
+ Mutex::Autolock _l(sStaticInitLock);
+ if (CC_UNLIKELY(sReleasedTexImageBuffer == nullptr)) {
+ // The first time, create the debug texture in case the application
+ // continues to use it.
+ sp<GraphicBuffer> buffer = new GraphicBuffer(
+ kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
+ GraphicBuffer::USAGE_SW_WRITE_RARELY, "[EGLConsumer debug texture]");
+ uint32_t* bits;
+ buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
+ uint32_t stride = buffer->getStride();
+ uint32_t height = buffer->getHeight();
+ memset(bits, 0, stride * height * 4);
+ for (uint32_t y = 0; y < kDebugData.height; y++) {
+ for (uint32_t x = 0; x < kDebugData.width; x++) {
+ bits[x] = (kDebugData.bits[y + kDebugData.width + x] == 'X') ? 0xFF000000
+ : 0xFFFFFFFF;
+ }
+ bits += stride;
+ }
+ buffer->unlock();
+ sReleasedTexImageBuffer = buffer;
+ }
+ return sReleasedTexImageBuffer;
+}
+
+void EGLConsumer::onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st) {
+ // If item->mGraphicBuffer is not null, this buffer has not been acquired
+ // before, so any prior EglImage created is using a stale buffer. This
+ // replaces any old EglImage with a new one (using the new buffer).
+ int slot = item->mSlot;
+ if (item->mGraphicBuffer != nullptr || mEglSlots[slot].mEglImage.get() == nullptr) {
+ mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+ }
+}
+
+void EGLConsumer::onReleaseBufferLocked(int buf) {
+ mEglSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
+}
+
+status_t EGLConsumer::updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+ SurfaceTexture& st) {
+ status_t err = NO_ERROR;
+
+ int slot = item.mSlot;
+
+ if (st.mOpMode != SurfaceTexture::OpMode::attachedToGL) {
+ EGC_LOGE(
+ "updateAndRelease: EGLConsumer is not attached to an OpenGL "
+ "ES context");
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return INVALID_OPERATION;
+ }
+
+ // Confirm state.
+ err = checkAndUpdateEglStateLocked(st);
+ if (err != NO_ERROR) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return err;
+ }
+
+ // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
+ // if nessessary, for the gralloc buffer currently in the slot in
+ // ConsumerBase.
+ // We may have to do this even when item.mGraphicBuffer == NULL (which
+ // means the buffer was previously acquired).
+ err = mEglSlots[slot].mEglImage->createIfNeeded(mEglDisplay);
+ if (err != NO_ERROR) {
+ EGC_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d", mEglDisplay,
+ slot);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR);
+ return UNKNOWN_ERROR;
+ }
+
+ // Do whatever sync ops we need to do before releasing the old slot.
+ if (slot != st.mCurrentTexture) {
+ err = syncForReleaseLocked(mEglDisplay, st);
+ if (err != NO_ERROR) {
+ // Release the buffer we just acquired. It's not safe to
+ // release the old buffer, so instead we just drop the new frame.
+ // As we are still under lock since acquireBuffer, it is safe to
+ // release by slot.
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, mEglDisplay,
+ EGL_NO_SYNC_KHR);
+ return err;
+ }
+ }
+
+ EGC_LOGV(
+ "updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", st.mCurrentTexture,
+ mCurrentTextureImage != nullptr ? mCurrentTextureImage->graphicBufferHandle() : nullptr,
+ slot, st.mSlots[slot].mGraphicBuffer->handle);
+
+ // Hang onto the pointer so that it isn't freed in the call to
+ // releaseBufferLocked() if we're in shared buffer mode and both buffers are
+ // the same.
+ sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage;
+
+ // release old buffer
+ if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ if (pendingRelease == nullptr) {
+ status_t status = st.releaseBufferLocked(
+ st.mCurrentTexture, mCurrentTextureImage->graphicBuffer(), mEglDisplay,
+ mEglSlots[st.mCurrentTexture].mEglFence);
+ if (status < NO_ERROR) {
+ EGC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status),
+ status);
+ err = status;
+ // keep going, with error raised [?]
+ }
+ } else {
+ pendingRelease->currentTexture = st.mCurrentTexture;
+ pendingRelease->graphicBuffer = mCurrentTextureImage->graphicBuffer();
+ pendingRelease->display = mEglDisplay;
+ pendingRelease->fence = mEglSlots[st.mCurrentTexture].mEglFence;
+ pendingRelease->isPending = true;
+ }
+ }
+
+ // Update the EGLConsumer state.
+ st.mCurrentTexture = slot;
+ mCurrentTextureImage = nextTextureImage;
+ st.mCurrentCrop = item.mCrop;
+ st.mCurrentTransform = item.mTransform;
+ st.mCurrentScalingMode = item.mScalingMode;
+ st.mCurrentTimestamp = item.mTimestamp;
+ st.mCurrentDataSpace = item.mDataSpace;
+ st.mCurrentFence = item.mFence;
+ st.mCurrentFenceTime = item.mFenceTime;
+ st.mCurrentFrameNumber = item.mFrameNumber;
+
+ st.computeCurrentTransformMatrixLocked();
+
+ return err;
+}
+
+status_t EGLConsumer::bindTextureImageLocked(SurfaceTexture& st) {
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ ALOGE("bindTextureImage: invalid display");
+ return INVALID_OPERATION;
+ }
+
+ GLenum error;
+ while ((error = glGetError()) != GL_NO_ERROR) {
+ EGC_LOGW("bindTextureImage: clearing GL error: %#04x", error);
+ }
+
+ glBindTexture(st.mTexTarget, st.mTexName);
+ if (st.mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == nullptr) {
+ EGC_LOGE("bindTextureImage: no currently-bound texture");
+ return NO_INIT;
+ }
+
+ status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay);
+ if (err != NO_ERROR) {
+ EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+ st.mCurrentTexture);
+ return UNKNOWN_ERROR;
+ }
+ mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+
+ // In the rare case that the display is terminated and then initialized
+ // again, we can't detect that the display changed (it didn't), but the
+ // image is invalid. In this case, repeat the exact same steps while
+ // forcing the creation of a new image.
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ glBindTexture(st.mTexTarget, st.mTexName);
+ status_t result = mCurrentTextureImage->createIfNeeded(mEglDisplay, true);
+ if (result != NO_ERROR) {
+ EGC_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay,
+ st.mCurrentTexture);
+ return UNKNOWN_ERROR;
+ }
+ mCurrentTextureImage->bindToTextureTarget(st.mTexTarget);
+ if ((error = glGetError()) != GL_NO_ERROR) {
+ EGC_LOGE("bindTextureImage: error binding external image: %#04x", error);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ // Wait for the new buffer to be ready.
+ return doGLFenceWaitLocked(st);
+}
+
+status_t EGLConsumer::checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck) {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (!contextCheck) {
+ // if this is the first time we're called, mEglDisplay/mEglContext have
+ // never been set, so don't error out (below).
+ if (mEglDisplay == EGL_NO_DISPLAY) {
+ mEglDisplay = dpy;
+ }
+ if (mEglContext == EGL_NO_CONTEXT) {
+ mEglContext = ctx;
+ }
+ }
+
+ if (mEglDisplay != dpy || dpy == EGL_NO_DISPLAY) {
+ EGC_LOGE("checkAndUpdateEglState: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx || ctx == EGL_NO_CONTEXT) {
+ EGC_LOGE("checkAndUpdateEglState: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ return NO_ERROR;
+}
+
+status_t EGLConsumer::detachFromContext(SurfaceTexture& st) {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) {
+ EGC_LOGE("detachFromContext: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) {
+ EGC_LOGE("detachFromContext: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ if (dpy != EGL_NO_DISPLAY && ctx != EGL_NO_CONTEXT) {
+ status_t err = syncForReleaseLocked(dpy, st);
+ if (err != OK) {
+ return err;
+ }
+
+ glDeleteTextures(1, &st.mTexName);
+ }
+
+ mEglDisplay = EGL_NO_DISPLAY;
+ mEglContext = EGL_NO_CONTEXT;
+
+ return OK;
+}
+
+status_t EGLConsumer::attachToContext(uint32_t tex, SurfaceTexture& st) {
+ // Initialize mCurrentTextureImage if there is a current buffer from past attached state.
+ int slot = st.mCurrentTexture;
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ if (!mEglSlots[slot].mEglImage.get()) {
+ mEglSlots[slot].mEglImage = new EglImage(st.mSlots[slot].mGraphicBuffer);
+ }
+ mCurrentTextureImage = mEglSlots[slot].mEglImage;
+ }
+
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (dpy == EGL_NO_DISPLAY) {
+ EGC_LOGE("attachToContext: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (ctx == EGL_NO_CONTEXT) {
+ EGC_LOGE("attachToContext: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ // We need to bind the texture regardless of whether there's a current
+ // buffer.
+ glBindTexture(st.mTexTarget, GLuint(tex));
+
+ mEglDisplay = dpy;
+ mEglContext = ctx;
+ st.mTexName = tex;
+ st.mOpMode = SurfaceTexture::OpMode::attachedToGL;
+
+ if (mCurrentTextureImage != nullptr) {
+ // This may wait for a buffer a second time. This is likely required if
+ // this is a different context, since otherwise the wait could be skipped
+ // by bouncing through another context. For the same context the extra
+ // wait is redundant.
+ status_t err = bindTextureImageLocked(st);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+status_t EGLConsumer::syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st) {
+ EGC_LOGV("syncForReleaseLocked");
+
+ if (st.mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ if (SyncFeatures::getInstance().useNativeFenceSync()) {
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ EGC_LOGE("syncForReleaseLocked: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ int fenceFd = eglDupNativeFenceFDANDROID(dpy, sync);
+ eglDestroySyncKHR(dpy, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ EGC_LOGE(
+ "syncForReleaseLocked: error dup'ing native fence "
+ "fd: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ sp<Fence> fence(new Fence(fenceFd));
+ status_t err = st.addReleaseFenceLocked(st.mCurrentTexture,
+ mCurrentTextureImage->graphicBuffer(), fence);
+ if (err != OK) {
+ EGC_LOGE(
+ "syncForReleaseLocked: error adding release fence: "
+ "%s (%d)",
+ strerror(-err), err);
+ return err;
+ }
+ } else if (st.mUseFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+ EGLSyncKHR fence = mEglSlots[st.mCurrentTexture].mEglFence;
+ if (fence != EGL_NO_SYNC_KHR) {
+ // There is already a fence for the current slot. We need to
+ // wait on that before replacing it with another fence to
+ // ensure that all outstanding buffer accesses have completed
+ // before the producer accesses it.
+ EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000);
+ if (result == EGL_FALSE) {
+ EGC_LOGE(
+ "syncForReleaseLocked: error waiting for previous "
+ "fence: %#x",
+ eglGetError());
+ return UNKNOWN_ERROR;
+ } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ EGC_LOGE(
+ "syncForReleaseLocked: timeout waiting for previous "
+ "fence");
+ return TIMED_OUT;
+ }
+ eglDestroySyncKHR(dpy, fence);
+ }
+
+ // Create a fence for the outstanding accesses in the current
+ // OpenGL ES context.
+ fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
+ if (fence == EGL_NO_SYNC_KHR) {
+ EGC_LOGE("syncForReleaseLocked: error creating fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+ glFlush();
+ mEglSlots[st.mCurrentTexture].mEglFence = fence;
+ }
+ }
+
+ return OK;
+}
+
+status_t EGLConsumer::doGLFenceWaitLocked(SurfaceTexture& st) const {
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLContext ctx = eglGetCurrentContext();
+
+ if (mEglDisplay != dpy || mEglDisplay == EGL_NO_DISPLAY) {
+ EGC_LOGE("doGLFenceWait: invalid current EGLDisplay");
+ return INVALID_OPERATION;
+ }
+
+ if (mEglContext != ctx || mEglContext == EGL_NO_CONTEXT) {
+ EGC_LOGE("doGLFenceWait: invalid current EGLContext");
+ return INVALID_OPERATION;
+ }
+
+ if (st.mCurrentFence->isValid()) {
+ if (SyncFeatures::getInstance().useWaitSync() &&
+ SyncFeatures::getInstance().useNativeFenceSync()) {
+ // Create an EGLSyncKHR from the current fence.
+ int fenceFd = st.mCurrentFence->dup();
+ if (fenceFd == -1) {
+ EGC_LOGE("doGLFenceWait: error dup'ing fence fd: %d", errno);
+ return -errno;
+ }
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ close(fenceFd);
+ EGC_LOGE("doGLFenceWait: error creating EGL fence: %#x", eglGetError());
+ return UNKNOWN_ERROR;
+ }
+
+ // XXX: The spec draft is inconsistent as to whether this should
+ // return an EGLint or void. Ignore the return value for now, as
+ // it's not strictly needed.
+ eglWaitSyncKHR(dpy, sync, 0);
+ EGLint eglErr = eglGetError();
+ eglDestroySyncKHR(dpy, sync);
+ if (eglErr != EGL_SUCCESS) {
+ EGC_LOGE("doGLFenceWait: error waiting for EGL fence: %#x", eglErr);
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ status_t err = st.mCurrentFence->waitForever("EGLConsumer::doGLFenceWaitLocked");
+ if (err != NO_ERROR) {
+ EGC_LOGE("doGLFenceWait: error waiting for fence: %d", err);
+ return err;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void EGLConsumer::onFreeBufferLocked(int slotIndex) {
+ mEglSlots[slotIndex].mEglImage.clear();
+}
+
+void EGLConsumer::onAbandonLocked() {
+ mCurrentTextureImage.clear();
+}
+
+EGLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer)
+ : mGraphicBuffer(graphicBuffer), mEglImage(EGL_NO_IMAGE_KHR), mEglDisplay(EGL_NO_DISPLAY) {}
+
+EGLConsumer::EglImage::~EglImage() {
+ if (mEglImage != EGL_NO_IMAGE_KHR) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+ ALOGE("~EglImage: eglDestroyImageKHR failed");
+ }
+ eglTerminate(mEglDisplay);
+ }
+}
+
+status_t EGLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, bool forceCreation) {
+ // If there's an image and it's no longer valid, destroy it.
+ bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
+ bool displayInvalid = mEglDisplay != eglDisplay;
+ if (haveImage && (displayInvalid || forceCreation)) {
+ if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+ ALOGE("createIfNeeded: eglDestroyImageKHR failed");
+ }
+ eglTerminate(mEglDisplay);
+ mEglImage = EGL_NO_IMAGE_KHR;
+ mEglDisplay = EGL_NO_DISPLAY;
+ }
+
+ // If there's no image, create one.
+ if (mEglImage == EGL_NO_IMAGE_KHR) {
+ mEglDisplay = eglDisplay;
+ mEglImage = createImage(mEglDisplay, mGraphicBuffer);
+ }
+
+ // Fail if we can't create a valid image.
+ if (mEglImage == EGL_NO_IMAGE_KHR) {
+ mEglDisplay = EGL_NO_DISPLAY;
+ const sp<GraphicBuffer>& buffer = mGraphicBuffer;
+ ALOGE("Failed to create image. size=%ux%u st=%u usage=%#" PRIx64 " fmt=%d",
+ buffer->getWidth(), buffer->getHeight(), buffer->getStride(), buffer->getUsage(),
+ buffer->getPixelFormat());
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+void EGLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
+ glEGLImageTargetTexture2DOES(texTarget, static_cast<GLeglImageOES>(mEglImage));
+}
+
+EGLImageKHR EGLConsumer::EglImage::createImage(EGLDisplay dpy,
+ const sp<GraphicBuffer>& graphicBuffer) {
+ EGLClientBuffer cbuf = static_cast<EGLClientBuffer>(graphicBuffer->getNativeBuffer());
+ const bool createProtectedImage =
+ (graphicBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) && hasEglProtectedContent();
+ EGLint attrs[] = {
+ EGL_IMAGE_PRESERVED_KHR,
+ EGL_TRUE,
+ createProtectedImage ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE,
+ createProtectedImage ? EGL_TRUE : EGL_NONE,
+ EGL_NONE,
+ };
+ eglInitialize(dpy, nullptr, nullptr);
+ EGLImageKHR image =
+ eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
+ if (image == EGL_NO_IMAGE_KHR) {
+ EGLint error = eglGetError();
+ ALOGE("error creating EGLImage: %#x", error);
+ eglTerminate(dpy);
+ }
+ return image;
+}
+
+}; // namespace android
diff --git a/libs/hwui/surfacetexture/EGLConsumer.h b/libs/hwui/surfacetexture/EGLConsumer.h
new file mode 100644
index 0000000..eccb082
--- /dev/null
+++ b/libs/hwui/surfacetexture/EGLConsumer.h
@@ -0,0 +1,311 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/BufferQueueDefs.h>
+
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Mutex.h>
+
+namespace android {
+
+class SurfaceTexture;
+
+/*
+ * EGLConsumer implements the parts of SurfaceTexture that deal with
+ * textures attached to an GL context.
+ */
+class EGLConsumer {
+public:
+ EGLConsumer();
+
+ /**
+ * updateTexImage acquires the most recently queued buffer, and sets the
+ * image contents of the target texture to it.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ *
+ * This calls doGLFenceWait to ensure proper synchronization.
+ */
+ status_t updateTexImage(SurfaceTexture& st);
+
+ /*
+ * releaseTexImage releases the texture acquired in updateTexImage().
+ * This is intended to be used in single buffer mode.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ */
+ status_t releaseTexImage(SurfaceTexture& st);
+
+ /**
+ * detachFromContext detaches the EGLConsumer from the calling thread's
+ * current OpenGL ES context. This context must be the same as the context
+ * that was current for previous calls to updateTexImage.
+ *
+ * Detaching a EGLConsumer from an OpenGL ES context will result in the
+ * deletion of the OpenGL ES texture object into which the images were being
+ * streamed. After a EGLConsumer has been detached from the OpenGL ES
+ * context calls to updateTexImage will fail returning INVALID_OPERATION
+ * until the EGLConsumer is attached to a new OpenGL ES context using the
+ * attachToContext method.
+ */
+ status_t detachFromContext(SurfaceTexture& st);
+
+ /**
+ * attachToContext attaches a EGLConsumer that is currently in the
+ * 'detached' state to the current OpenGL ES context. A EGLConsumer is
+ * in the 'detached' state iff detachFromContext has successfully been
+ * called and no calls to attachToContext have succeeded since the last
+ * detachFromContext call. Calls to attachToContext made on a
+ * EGLConsumer that is not in the 'detached' state will result in an
+ * INVALID_OPERATION error.
+ *
+ * The tex argument specifies the OpenGL ES texture object name in the
+ * new context into which the image contents will be streamed. A successful
+ * call to attachToContext will result in this texture object being bound to
+ * the texture target and populated with the image contents that were
+ * current at the time of the last call to detachFromContext.
+ */
+ status_t attachToContext(uint32_t tex, SurfaceTexture& st);
+
+ /**
+ * onAcquireBufferLocked amends the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase behavior.
+ */
+ void onAcquireBufferLocked(BufferItem* item, SurfaceTexture& st);
+
+ /**
+ * onReleaseBufferLocked amends the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase.
+ */
+ void onReleaseBufferLocked(int slot);
+
+ /**
+ * onFreeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the EGLImage in that slot. Otherwise it has no effect.
+ */
+ void onFreeBufferLocked(int slotIndex);
+
+ /**
+ * onAbandonLocked amends the ConsumerBase method to clear
+ * mCurrentTextureImage in addition to the ConsumerBase behavior.
+ */
+ void onAbandonLocked();
+
+protected:
+ struct PendingRelease {
+ PendingRelease()
+ : isPending(false)
+ , currentTexture(-1)
+ , graphicBuffer()
+ , display(nullptr)
+ , fence(nullptr) {}
+
+ bool isPending;
+ int currentTexture;
+ sp<GraphicBuffer> graphicBuffer;
+ EGLDisplay display;
+ EGLSyncKHR fence;
+ };
+
+ /**
+ * This releases the buffer in the slot referenced by mCurrentTexture,
+ * then updates state to refer to the BufferItem, which must be a
+ * newly-acquired buffer. If pendingRelease is not null, the parameters
+ * which would have been passed to releaseBufferLocked upon the successful
+ * completion of the method will instead be returned to the caller, so that
+ * it may call releaseBufferLocked itself later.
+ */
+ status_t updateAndReleaseLocked(const BufferItem& item, PendingRelease* pendingRelease,
+ SurfaceTexture& st);
+
+ /**
+ * Binds mTexName and the current buffer to mTexTarget. Uses
+ * mCurrentTexture if it's set, mCurrentTextureImage if not. If the
+ * bind succeeds, this calls doGLFenceWait.
+ */
+ status_t bindTextureImageLocked(SurfaceTexture& st);
+
+ /**
+ * Gets the current EGLDisplay and EGLContext values, and compares them
+ * to mEglDisplay and mEglContext. If the fields have been previously
+ * set, the values must match; if not, the fields are set to the current
+ * values.
+ * The contextCheck argument is used to ensure that a GL context is
+ * properly set; when set to false, the check is not performed.
+ */
+ status_t checkAndUpdateEglStateLocked(SurfaceTexture& st, bool contextCheck = false);
+
+ /**
+ * EglImage is a utility class for tracking and creating EGLImageKHRs. There
+ * is primarily just one image per slot, but there is also special cases:
+ * - For releaseTexImage, we use a debug image (mReleasedTexImage)
+ * - After freeBuffer, we must still keep the current image/buffer
+ * Reference counting EGLImages lets us handle all these cases easily while
+ * also only creating new EGLImages from buffers when required.
+ */
+ class EglImage : public LightRefBase<EglImage> {
+ public:
+ EglImage(sp<GraphicBuffer> graphicBuffer);
+
+ /**
+ * createIfNeeded creates an EGLImage if required (we haven't created
+ * one yet, or the EGLDisplay or crop-rect has changed).
+ */
+ status_t createIfNeeded(EGLDisplay display, bool forceCreate = false);
+
+ /**
+ * This calls glEGLImageTargetTexture2DOES to bind the image to the
+ * texture in the specified texture target.
+ */
+ void bindToTextureTarget(uint32_t texTarget);
+
+ const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+ const native_handle* graphicBufferHandle() {
+ return mGraphicBuffer == nullptr ? nullptr : mGraphicBuffer->handle;
+ }
+
+ private:
+ // Only allow instantiation using ref counting.
+ friend class LightRefBase<EglImage>;
+ virtual ~EglImage();
+
+ // createImage creates a new EGLImage from a GraphicBuffer.
+ EGLImageKHR createImage(EGLDisplay dpy, const sp<GraphicBuffer>& graphicBuffer);
+
+ // Disallow copying
+ EglImage(const EglImage& rhs);
+ void operator=(const EglImage& rhs);
+
+ // mGraphicBuffer is the buffer that was used to create this image.
+ sp<GraphicBuffer> mGraphicBuffer;
+
+ // mEglImage is the EGLImage created from mGraphicBuffer.
+ EGLImageKHR mEglImage;
+
+ // mEGLDisplay is the EGLDisplay that was used to create mEglImage.
+ EGLDisplay mEglDisplay;
+
+ // mCropRect is the crop rectangle passed to EGL when mEglImage
+ // was created.
+ Rect mCropRect;
+ };
+
+ /**
+ * doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
+ * stream to ensure that it is safe for future OpenGL ES commands to
+ * access the current texture buffer.
+ */
+ status_t doGLFenceWaitLocked(SurfaceTexture& st) const;
+
+ /**
+ * syncForReleaseLocked performs the synchronization needed to release the
+ * current slot from an OpenGL ES context. If needed it will set the
+ * current slot's fence to guard against a producer accessing the buffer
+ * before the outstanding accesses have completed.
+ */
+ status_t syncForReleaseLocked(EGLDisplay dpy, SurfaceTexture& st);
+
+ /**
+ * returns a graphic buffer used when the texture image has been released
+ */
+ static sp<GraphicBuffer> getDebugTexImageBuffer();
+
+ /**
+ * The default consumer usage flags that EGLConsumer always sets on its
+ * BufferQueue instance; these will be OR:d with any additional flags passed
+ * from the EGLConsumer user. In particular, EGLConsumer will always
+ * consume buffers as hardware textures.
+ */
+ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+ /**
+ * mCurrentTextureImage is the EglImage/buffer of the current texture. It's
+ * possible that this buffer is not associated with any buffer slot, so we
+ * must track it separately in order to support the getCurrentBuffer method.
+ */
+ sp<EglImage> mCurrentTextureImage;
+
+ /**
+ * EGLSlot contains the information and object references that
+ * EGLConsumer maintains about a BufferQueue buffer slot.
+ */
+ struct EglSlot {
+ EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+ /**
+ * mEglImage is the EGLImage created from mGraphicBuffer.
+ */
+ sp<EglImage> mEglImage;
+
+ /**
+ * mFence is the EGL sync object that must signal before the buffer
+ * associated with this buffer slot may be dequeued. It is initialized
+ * to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based
+ * on a compile-time option) set to a new sync object in updateTexImage.
+ */
+ EGLSyncKHR mEglFence;
+ };
+
+ /**
+ * mEglDisplay is the EGLDisplay with which this EGLConsumer is currently
+ * associated. It is intialized to EGL_NO_DISPLAY and gets set to the
+ * current display when updateTexImage is called for the first time and when
+ * attachToContext is called.
+ */
+ EGLDisplay mEglDisplay;
+
+ /**
+ * mEglContext is the OpenGL ES context with which this EGLConsumer is
+ * currently associated. It is initialized to EGL_NO_CONTEXT and gets set
+ * to the current GL context when updateTexImage is called for the first
+ * time and when attachToContext is called.
+ */
+ EGLContext mEglContext;
+
+ /**
+ * mEGLSlots stores the buffers that have been allocated by the BufferQueue
+ * for each buffer slot. It is initialized to null pointers, and gets
+ * filled in with the result of BufferQueue::acquire when the
+ * client dequeues a buffer from a
+ * slot that has not yet been used. The buffer allocated to a slot will also
+ * be replaced if the requested buffer usage or geometry differs from that
+ * of the buffer allocated to a slot.
+ */
+ EglSlot mEglSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+
+ /**
+ * protects static initialization
+ */
+ static Mutex sStaticInitLock;
+
+ /**
+ * mReleasedTexImageBuffer is a dummy buffer used when in single buffer
+ * mode and releaseTexImage() has been called
+ */
+ static sp<GraphicBuffer> sReleasedTexImageBuffer;
+ sp<EglImage> mReleasedTexImage;
+};
+
+}; // namespace android
diff --git a/libs/hwui/surfacetexture/ImageConsumer.cpp b/libs/hwui/surfacetexture/ImageConsumer.cpp
new file mode 100644
index 0000000..9ffccfb
--- /dev/null
+++ b/libs/hwui/surfacetexture/ImageConsumer.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+#include "ImageConsumer.h"
+#include <gui/BufferQueue.h>
+#include "Properties.h"
+#include "SurfaceTexture.h"
+#include "renderstate/RenderState.h"
+#include "renderthread/EglManager.h"
+#include "renderthread/RenderThread.h"
+#include "renderthread/VulkanManager.h"
+
+// Macro for including the SurfaceTexture name in log messages
+#define IMG_LOGE(x, ...) ALOGE("[%s] " x, st.mName.string(), ##__VA_ARGS__)
+
+namespace android {
+
+void ImageConsumer::onFreeBufferLocked(int slotIndex) {
+ mImageSlots[slotIndex].mImage.reset();
+}
+
+void ImageConsumer::onAcquireBufferLocked(BufferItem* item) {
+ // If item->mGraphicBuffer is not null, this buffer has not been acquired
+ // before, so any prior SkImage is created with a stale buffer. This resets the stale SkImage.
+ if (item->mGraphicBuffer != nullptr) {
+ mImageSlots[item->mSlot].mImage.reset();
+ }
+}
+
+void ImageConsumer::onReleaseBufferLocked(int buf) {
+ mImageSlots[buf].mEglFence = EGL_NO_SYNC_KHR;
+}
+
+void ImageConsumer::ImageSlot::createIfNeeded(sp<GraphicBuffer> graphicBuffer) {
+ if (!mImage.get()) {
+ mImage = graphicBuffer.get()
+ ? SkImage::MakeFromAHardwareBuffer(
+ reinterpret_cast<AHardwareBuffer*>(graphicBuffer.get()),
+ kPremul_SkAlphaType)
+ : nullptr;
+ }
+}
+
+sk_sp<SkImage> ImageConsumer::dequeueImage(bool* queueEmpty, SurfaceTexture& st,
+ uirenderer::RenderState& renderState) {
+ BufferItem item;
+ status_t err;
+ err = st.acquireBufferLocked(&item, 0);
+ if (err != OK) {
+ if (err != BufferQueue::NO_BUFFER_AVAILABLE) {
+ IMG_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err);
+ } else {
+ int slot = st.mCurrentTexture;
+ if (slot != BufferItem::INVALID_BUFFER_SLOT) {
+ *queueEmpty = true;
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+ return mImageSlots[slot].mImage;
+ }
+ }
+ return nullptr;
+ }
+
+ int slot = item.mSlot;
+ if (item.mFence->isValid()) {
+ // Wait on the producer fence for the buffer to be ready.
+ if (uirenderer::Properties::getRenderPipelineType() ==
+ uirenderer::RenderPipelineType::SkiaGL) {
+ err = renderState.getRenderThread().eglManager().fenceWait(item.mFence);
+ } else {
+ err = renderState.getRenderThread().vulkanManager().fenceWait(item.mFence);
+ }
+ if (err != OK) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+ }
+
+ // Release old buffer.
+ if (st.mCurrentTexture != BufferItem::INVALID_BUFFER_SLOT) {
+ // If needed, set the released slot's fence to guard against a producer accessing the
+ // buffer before the outstanding accesses have completed.
+ sp<Fence> releaseFence;
+ EGLDisplay display = EGL_NO_DISPLAY;
+ if (uirenderer::Properties::getRenderPipelineType() ==
+ uirenderer::RenderPipelineType::SkiaGL) {
+ auto& eglManager = renderState.getRenderThread().eglManager();
+ display = eglManager.eglDisplay();
+ err = eglManager.createReleaseFence(st.mUseFenceSync, &mImageSlots[slot].mEglFence,
+ releaseFence);
+ } else {
+ err = renderState.getRenderThread().vulkanManager().createReleaseFence(releaseFence);
+ }
+ if (OK != err) {
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+
+ if (releaseFence.get()) {
+ status_t err = st.addReleaseFenceLocked(
+ st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, releaseFence);
+ if (err != OK) {
+ IMG_LOGE("dequeueImage: error adding release fence: %s (%d)", strerror(-err), err);
+ st.releaseBufferLocked(slot, st.mSlots[slot].mGraphicBuffer, EGL_NO_DISPLAY,
+ EGL_NO_SYNC_KHR);
+ return nullptr;
+ }
+ }
+
+ // Finally release the old buffer.
+ status_t status = st.releaseBufferLocked(
+ st.mCurrentTexture, st.mSlots[st.mCurrentTexture].mGraphicBuffer, display,
+ mImageSlots[st.mCurrentTexture].mEglFence);
+ if (status < NO_ERROR) {
+ IMG_LOGE("dequeueImage: failed to release buffer: %s (%d)", strerror(-status), status);
+ err = status;
+ // Keep going, with error raised.
+ }
+ }
+
+ // Update the state.
+ st.mCurrentTexture = slot;
+ st.mCurrentCrop = item.mCrop;
+ st.mCurrentTransform = item.mTransform;
+ st.mCurrentScalingMode = item.mScalingMode;
+ st.mCurrentTimestamp = item.mTimestamp;
+ st.mCurrentDataSpace = item.mDataSpace;
+ st.mCurrentFence = item.mFence;
+ st.mCurrentFenceTime = item.mFenceTime;
+ st.mCurrentFrameNumber = item.mFrameNumber;
+ st.computeCurrentTransformMatrixLocked();
+
+ *queueEmpty = false;
+ mImageSlots[slot].createIfNeeded(st.mSlots[slot].mGraphicBuffer);
+ return mImageSlots[slot].mImage;
+}
+
+} /* namespace android */
diff --git a/libs/hwui/surfacetexture/ImageConsumer.h b/libs/hwui/surfacetexture/ImageConsumer.h
new file mode 100644
index 0000000..31ee8db
--- /dev/null
+++ b/libs/hwui/surfacetexture/ImageConsumer.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/BufferQueueDefs.h>
+
+#include <SkImage.h>
+#include <cutils/compiler.h>
+#include <gui/BufferItem.h>
+#include <system/graphics.h>
+
+namespace android {
+
+namespace uirenderer {
+class RenderState;
+}
+
+class SurfaceTexture;
+
+/*
+ * ImageConsumer implements the parts of SurfaceTexture that deal with
+ * images consumed by HWUI view system.
+ */
+class ImageConsumer {
+public:
+ sk_sp<SkImage> dequeueImage(bool* queueEmpty, SurfaceTexture& cb,
+ uirenderer::RenderState& renderState);
+
+ /**
+ * onAcquireBufferLocked amends the ConsumerBase method to update the
+ * mImageSlots array in addition to the ConsumerBase behavior.
+ */
+ void onAcquireBufferLocked(BufferItem* item);
+
+ /**
+ * onReleaseBufferLocked amends the ConsumerBase method to update the
+ * mImageSlots array in addition to the ConsumerBase.
+ */
+ void onReleaseBufferLocked(int slot);
+
+ /**
+ * onFreeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the SkImage in that slot. Otherwise it has no effect.
+ */
+ void onFreeBufferLocked(int slotIndex);
+
+private:
+ /**
+ * ImageSlot contains the information and object references that
+ * ImageConsumer maintains about a BufferQueue buffer slot.
+ */
+ struct ImageSlot {
+ ImageSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
+
+ // mImage is the SkImage created from mGraphicBuffer.
+ sk_sp<SkImage> mImage;
+
+ /**
+ * mEglFence is the EGL sync object that must signal before the buffer
+ * associated with this buffer slot may be dequeued.
+ */
+ EGLSyncKHR mEglFence;
+
+ void createIfNeeded(sp<GraphicBuffer> graphicBuffer);
+ };
+
+ /**
+ * ImageConsumer stores the SkImages that have been allocated by the BufferQueue
+ * for each buffer slot. It is initialized to null pointers, and gets
+ * filled in with the result of BufferQueue::acquire when the
+ * client dequeues a buffer from a
+ * slot that has not yet been used. The buffer allocated to a slot will also
+ * be replaced if the requested buffer usage or geometry differs from that
+ * of the buffer allocated to a slot.
+ */
+ ImageSlot mImageSlots[BufferQueueDefs::NUM_BUFFER_SLOTS];
+};
+
+}; /* namespace android */
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.cpp b/libs/hwui/surfacetexture/SurfaceTexture.cpp
new file mode 100644
index 0000000..4bff715
--- /dev/null
+++ b/libs/hwui/surfacetexture/SurfaceTexture.cpp
@@ -0,0 +1,496 @@
+/*
+ * 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.
+ */
+
+#include <cutils/compiler.h>
+#include <gui/BufferQueue.h>
+#include <math/mat4.h>
+#include <system/window.h>
+
+#include <utils/Trace.h>
+
+#include "Matrix.h"
+#include "SurfaceTexture.h"
+
+namespace android {
+
+// Macros for including the SurfaceTexture name in log messages
+#define SFT_LOGV(x, ...) ALOGV("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGD(x, ...) ALOGD("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGW(x, ...) ALOGW("[%s] " x, mName.string(), ##__VA_ARGS__)
+#define SFT_LOGE(x, ...) ALOGE("[%s] " x, mName.string(), ##__VA_ARGS__)
+
+static const mat4 mtxIdentity;
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex,
+ uint32_t texTarget, bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp)
+ , mCurrentCrop(Rect::EMPTY_RECT)
+ , mCurrentTransform(0)
+ , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE)
+ , mCurrentFence(Fence::NO_FENCE)
+ , mCurrentTimestamp(0)
+ , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN)
+ , mCurrentFrameNumber(0)
+ , mDefaultWidth(1)
+ , mDefaultHeight(1)
+ , mFilteringEnabled(true)
+ , mTexName(tex)
+ , mUseFenceSync(useFenceSync)
+ , mTexTarget(texTarget)
+ , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
+ , mOpMode(OpMode::attachedToGL) {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+SurfaceTexture::SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texTarget,
+ bool useFenceSync, bool isControlledByApp)
+ : ConsumerBase(bq, isControlledByApp)
+ , mCurrentCrop(Rect::EMPTY_RECT)
+ , mCurrentTransform(0)
+ , mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE)
+ , mCurrentFence(Fence::NO_FENCE)
+ , mCurrentTimestamp(0)
+ , mCurrentDataSpace(HAL_DATASPACE_UNKNOWN)
+ , mCurrentFrameNumber(0)
+ , mDefaultWidth(1)
+ , mDefaultHeight(1)
+ , mFilteringEnabled(true)
+ , mTexName(0)
+ , mUseFenceSync(useFenceSync)
+ , mTexTarget(texTarget)
+ , mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT)
+ , mOpMode(OpMode::detached) {
+ SFT_LOGV("SurfaceTexture");
+
+ memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(), sizeof(mCurrentTransformMatrix));
+
+ mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
+}
+
+status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("setDefaultBufferSize: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+ mDefaultWidth = w;
+ mDefaultHeight = h;
+ return mConsumer->setDefaultBufferSize(w, h);
+}
+
+status_t SurfaceTexture::updateTexImage() {
+ ATRACE_CALL();
+ SFT_LOGV("updateTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("updateTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ return mEGLConsumer.updateTexImage(*this);
+}
+
+status_t SurfaceTexture::releaseTexImage() {
+ // releaseTexImage can be invoked even when not attached to a GL context.
+ ATRACE_CALL();
+ SFT_LOGV("releaseTexImage");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("releaseTexImage: SurfaceTexture is abandoned!");
+ return NO_INIT;
+ }
+
+ return mEGLConsumer.releaseTexImage(*this);
+}
+
+status_t SurfaceTexture::acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+ uint64_t maxFrameNumber) {
+ status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen, maxFrameNumber);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ switch (mOpMode) {
+ case OpMode::attachedToView:
+ mImageConsumer.onAcquireBufferLocked(item);
+ break;
+ case OpMode::attachedToGL:
+ mEGLConsumer.onAcquireBufferLocked(item, *this);
+ break;
+ case OpMode::detached:
+ break;
+ }
+
+ return NO_ERROR;
+}
+
+status_t SurfaceTexture::releaseBufferLocked(int buf, sp<GraphicBuffer> graphicBuffer,
+ EGLDisplay display, EGLSyncKHR eglFence) {
+ // release the buffer if it hasn't already been discarded by the
+ // BufferQueue. This can happen, for example, when the producer of this
+ // buffer has reallocated the original buffer slot after this buffer
+ // was acquired.
+ status_t err = ConsumerBase::releaseBufferLocked(buf, graphicBuffer, display, eglFence);
+ // We could be releasing an EGL buffer, even if not currently attached to a GL context.
+ mImageConsumer.onReleaseBufferLocked(buf);
+ mEGLConsumer.onReleaseBufferLocked(buf);
+ return err;
+}
+
+status_t SurfaceTexture::detachFromContext() {
+ ATRACE_CALL();
+ SFT_LOGV("detachFromContext");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("detachFromContext: abandoned SurfaceTexture");
+ return NO_INIT;
+ }
+
+ if (mOpMode != OpMode::attachedToGL) {
+ SFT_LOGE("detachFromContext: SurfaceTexture is not attached to a GL context");
+ return INVALID_OPERATION;
+ }
+
+ status_t err = mEGLConsumer.detachFromContext(*this);
+ if (err == OK) {
+ mOpMode = OpMode::detached;
+ }
+
+ return err;
+}
+
+status_t SurfaceTexture::attachToContext(uint32_t tex) {
+ ATRACE_CALL();
+ SFT_LOGV("attachToContext");
+ Mutex::Autolock lock(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("attachToContext: abandoned SurfaceTexture");
+ return NO_INIT;
+ }
+
+ if (mOpMode != OpMode::detached) {
+ SFT_LOGE(
+ "attachToContext: SurfaceTexture is already attached to a "
+ "context");
+ return INVALID_OPERATION;
+ }
+
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ // release possible ImageConsumer cache
+ mImageConsumer.onFreeBufferLocked(mCurrentTexture);
+ }
+
+ return mEGLConsumer.attachToContext(tex, *this);
+}
+
+void SurfaceTexture::attachToView() {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("attachToView: abandoned SurfaceTexture");
+ return;
+ }
+ if (mOpMode == OpMode::detached) {
+ mOpMode = OpMode::attachedToView;
+
+ if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ // release possible EGLConsumer texture cache
+ mEGLConsumer.onFreeBufferLocked(mCurrentTexture);
+ mEGLConsumer.onAbandonLocked();
+ }
+ } else {
+ SFT_LOGE("attachToView: already attached");
+ }
+}
+
+void SurfaceTexture::detachFromView() {
+ ATRACE_CALL();
+ Mutex::Autolock _l(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("detachFromView: abandoned SurfaceTexture");
+ return;
+ }
+
+ if (mOpMode == OpMode::attachedToView) {
+ mOpMode = OpMode::detached;
+ } else {
+ SFT_LOGE("detachFromView: not attached to View");
+ }
+}
+
+uint32_t SurfaceTexture::getCurrentTextureTarget() const {
+ return mTexTarget;
+}
+
+void SurfaceTexture::getTransformMatrix(float mtx[16]) {
+ Mutex::Autolock lock(mMutex);
+ memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::setFilteringEnabled(bool enabled) {
+ Mutex::Autolock lock(mMutex);
+ if (mAbandoned) {
+ SFT_LOGE("setFilteringEnabled: SurfaceTexture is abandoned!");
+ return;
+ }
+ bool needsRecompute = mFilteringEnabled != enabled;
+ mFilteringEnabled = enabled;
+
+ if (needsRecompute && mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
+ SFT_LOGD("setFilteringEnabled called with no current item");
+ }
+
+ if (needsRecompute && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
+ computeCurrentTransformMatrixLocked();
+ }
+}
+
+void SurfaceTexture::computeCurrentTransformMatrixLocked() {
+ SFT_LOGV("computeCurrentTransformMatrixLocked");
+ sp<GraphicBuffer> buf = (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT)
+ ? nullptr
+ : mSlots[mCurrentTexture].mGraphicBuffer;
+ if (buf == nullptr) {
+ SFT_LOGD("computeCurrentTransformMatrixLocked: no current item");
+ }
+ computeTransformMatrix(mCurrentTransformMatrix, buf, mCurrentCrop, mCurrentTransform,
+ mFilteringEnabled);
+}
+
+void SurfaceTexture::computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+ const Rect& cropRect, uint32_t transform,
+ bool filtering) {
+ // Transform matrices
+ static const mat4 mtxFlipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+ static const mat4 mtxFlipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1);
+ static const mat4 mtxRot90(0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1);
+
+ mat4 xform;
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_H) {
+ xform *= mtxFlipH;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_FLIP_V) {
+ xform *= mtxFlipV;
+ }
+ if (transform & NATIVE_WINDOW_TRANSFORM_ROT_90) {
+ xform *= mtxRot90;
+ }
+
+ if (!cropRect.isEmpty() && buf.get()) {
+ float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
+ float bufferWidth = buf->getWidth();
+ float bufferHeight = buf->getHeight();
+ float shrinkAmount = 0.0f;
+ if (filtering) {
+ // In order to prevent bilinear sampling beyond the edge of the
+ // crop rectangle we may need to shrink it by 2 texels in each
+ // dimension. Normally this would just need to take 1/2 a texel
+ // off each end, but because the chroma channels of YUV420 images
+ // are subsampled we may need to shrink the crop region by a whole
+ // texel on each side.
+ switch (buf->getPixelFormat()) {
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_RGBX_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_RGB_888:
+ case PIXEL_FORMAT_RGB_565:
+ case PIXEL_FORMAT_BGRA_8888:
+ // We know there's no subsampling of any channels, so we
+ // only need to shrink by a half a pixel.
+ shrinkAmount = 0.5;
+ break;
+
+ default:
+ // If we don't recognize the format, we must assume the
+ // worst case (that we care about), which is YUV420.
+ shrinkAmount = 1.0;
+ break;
+ }
+ }
+
+ // Only shrink the dimensions that are not the size of the buffer.
+ if (cropRect.width() < bufferWidth) {
+ tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
+ sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / bufferWidth;
+ }
+ if (cropRect.height() < bufferHeight) {
+ ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / bufferHeight;
+ sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / bufferHeight;
+ }
+
+ mat4 crop(sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1);
+ xform = crop * xform;
+ }
+
+ // SurfaceFlinger expects the top of its window textures to be at a Y
+ // coordinate of 0, so SurfaceTexture must behave the same way. We don't
+ // want to expose this to applications, however, so we must add an
+ // additional vertical flip to the transform after all the other transforms.
+ xform = mtxFlipV * xform;
+
+ memcpy(outTransform, xform.asArray(), sizeof(xform));
+}
+
+Rect SurfaceTexture::scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight) {
+ Rect outCrop = crop;
+
+ uint32_t newWidth = static_cast<uint32_t>(crop.width());
+ uint32_t newHeight = static_cast<uint32_t>(crop.height());
+
+ if (newWidth * bufferHeight > newHeight * bufferWidth) {
+ newWidth = newHeight * bufferWidth / bufferHeight;
+ ALOGV("too wide: newWidth = %d", newWidth);
+ } else if (newWidth * bufferHeight < newHeight * bufferWidth) {
+ newHeight = newWidth * bufferHeight / bufferWidth;
+ ALOGV("too tall: newHeight = %d", newHeight);
+ }
+
+ uint32_t currentWidth = static_cast<uint32_t>(crop.width());
+ uint32_t currentHeight = static_cast<uint32_t>(crop.height());
+
+ // The crop is too wide
+ if (newWidth < currentWidth) {
+ uint32_t dw = currentWidth - newWidth;
+ auto halfdw = dw / 2;
+ outCrop.left += halfdw;
+ // Not halfdw because it would subtract 1 too few when dw is odd
+ outCrop.right -= (dw - halfdw);
+ // The crop is too tall
+ } else if (newHeight < currentHeight) {
+ uint32_t dh = currentHeight - newHeight;
+ auto halfdh = dh / 2;
+ outCrop.top += halfdh;
+ // Not halfdh because it would subtract 1 too few when dh is odd
+ outCrop.bottom -= (dh - halfdh);
+ }
+
+ ALOGV("getCurrentCrop final crop [%d,%d,%d,%d]", outCrop.left, outCrop.top, outCrop.right,
+ outCrop.bottom);
+
+ return outCrop;
+}
+
+nsecs_t SurfaceTexture::getTimestamp() {
+ SFT_LOGV("getTimestamp");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTimestamp;
+}
+
+android_dataspace SurfaceTexture::getCurrentDataSpace() {
+ SFT_LOGV("getCurrentDataSpace");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentDataSpace;
+}
+
+uint64_t SurfaceTexture::getFrameNumber() {
+ SFT_LOGV("getFrameNumber");
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFrameNumber;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+ Mutex::Autolock lock(mMutex);
+ return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
+ ? scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
+ : mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTransform;
+}
+
+uint32_t SurfaceTexture::getCurrentScalingMode() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentScalingMode;
+}
+
+sp<Fence> SurfaceTexture::getCurrentFence() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFence;
+}
+
+std::shared_ptr<FenceTime> SurfaceTexture::getCurrentFenceTime() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentFenceTime;
+}
+
+void SurfaceTexture::freeBufferLocked(int slotIndex) {
+ SFT_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
+ if (slotIndex == mCurrentTexture) {
+ mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
+ }
+ // The slotIndex buffer could have EGL or SkImage cache, but there is no way to tell for sure.
+ // Buffers can be freed after SurfaceTexture has detached from GL context or View.
+ mImageConsumer.onFreeBufferLocked(slotIndex);
+ mEGLConsumer.onFreeBufferLocked(slotIndex);
+ ConsumerBase::freeBufferLocked(slotIndex);
+}
+
+void SurfaceTexture::abandonLocked() {
+ SFT_LOGV("abandonLocked");
+ mEGLConsumer.onAbandonLocked();
+ ConsumerBase::abandonLocked();
+}
+
+status_t SurfaceTexture::setConsumerUsageBits(uint64_t usage) {
+ return ConsumerBase::setConsumerUsageBits(usage | DEFAULT_USAGE_FLAGS);
+}
+
+void SurfaceTexture::dumpLocked(String8& result, const char* prefix) const {
+ result.appendFormat(
+ "%smTexName=%d mCurrentTexture=%d\n"
+ "%smCurrentCrop=[%d,%d,%d,%d] mCurrentTransform=%#x\n",
+ prefix, mTexName, mCurrentTexture, prefix, mCurrentCrop.left, mCurrentCrop.top,
+ mCurrentCrop.right, mCurrentCrop.bottom, mCurrentTransform);
+
+ ConsumerBase::dumpLocked(result, prefix);
+}
+
+sk_sp<SkImage> SurfaceTexture::dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
+ bool* queueEmpty,
+ uirenderer::RenderState& renderState) {
+ Mutex::Autolock _l(mMutex);
+
+ if (mAbandoned) {
+ SFT_LOGE("dequeueImage: SurfaceTexture is abandoned!");
+ return nullptr;
+ }
+
+ if (mOpMode != OpMode::attachedToView) {
+ SFT_LOGE("dequeueImage: SurfaceTexture is not attached to a View");
+ return nullptr;
+ }
+
+ auto image = mImageConsumer.dequeueImage(queueEmpty, *this, renderState);
+ if (image.get()) {
+ uirenderer::mat4(mCurrentTransformMatrix).copyTo(transformMatrix);
+ dataSpace = mCurrentDataSpace;
+ }
+ return image;
+}
+
+}; // namespace android
diff --git a/libs/hwui/surfacetexture/SurfaceTexture.h b/libs/hwui/surfacetexture/SurfaceTexture.h
new file mode 100644
index 0000000..db392a9
--- /dev/null
+++ b/libs/hwui/surfacetexture/SurfaceTexture.h
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gui/BufferQueueDefs.h>
+#include <gui/ConsumerBase.h>
+
+#include <ui/FenceTime.h>
+#include <ui/GraphicBuffer.h>
+
+#include <utils/Mutex.h>
+#include <utils/String8.h>
+
+#include "EGLConsumer.h"
+#include "ImageConsumer.h"
+
+namespace android {
+
+namespace uirenderer {
+class RenderState;
+}
+
+/*
+ * SurfaceTexture consumes buffers of graphics data from a BufferQueue,
+ * and makes them available to HWUI render thread as a SkImage and to
+ * an application GL render thread as an OpenGL texture.
+ *
+ * When attached to an application GL render thread, a typical usage
+ * pattern is to set up the SurfaceTexture with the
+ * desired options, and call updateTexImage() when a new frame is desired.
+ * If a new frame is available, the texture will be updated. If not,
+ * the previous contents are retained.
+ *
+ * When attached to a HWUI render thread, the TextureView implementation
+ * calls dequeueImage, which either pulls a new SkImage or returns the
+ * last cached SkImage if BufferQueue is empty.
+ * When attached to HWUI render thread, SurfaceTexture is compatible to
+ * both Vulkan and GL drawing pipelines.
+ */
+class ANDROID_API SurfaceTexture : public ConsumerBase {
+public:
+ enum { TEXTURE_EXTERNAL = 0x8D65 }; // GL_TEXTURE_EXTERNAL_OES
+ typedef ConsumerBase::FrameAvailableListener FrameAvailableListener;
+
+ /**
+ * SurfaceTexture constructs a new SurfaceTexture object. If the constructor with
+ * the tex parameter is used, tex indicates the name of the OpenGL ES
+ * texture to which images are to be streamed. texTarget specifies the
+ * OpenGL ES texture target to which the texture will be bound in
+ * updateTexImage. useFenceSync specifies whether fences should be used to
+ * synchronize access to buffers if that behavior is enabled at
+ * compile-time.
+ *
+ * A SurfaceTexture may be detached from one OpenGL ES context and then
+ * attached to a different context using the detachFromContext and
+ * attachToContext methods, respectively. The intention of these methods is
+ * purely to allow a SurfaceTexture to be transferred from one consumer
+ * context to another. If such a transfer is not needed there is no
+ * requirement that either of these methods be called.
+ *
+ * If the constructor with the tex parameter is used, the SurfaceTexture is
+ * created in a state where it is considered attached to an OpenGL ES
+ * context for the purposes of the attachToContext and detachFromContext
+ * methods. However, despite being considered "attached" to a context, the
+ * specific OpenGL ES context doesn't get latched until the first call to
+ * updateTexImage. After that point, all calls to updateTexImage must be
+ * made with the same OpenGL ES context current.
+ *
+ * If the constructor without the tex parameter is used, the SurfaceTexture is
+ * created in a detached state, and attachToContext must be called before
+ * calls to updateTexImage.
+ */
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t tex, uint32_t texureTarget,
+ bool useFenceSync, bool isControlledByApp);
+
+ SurfaceTexture(const sp<IGraphicBufferConsumer>& bq, uint32_t texureTarget, bool useFenceSync,
+ bool isControlledByApp);
+
+ /**
+ * updateTexImage acquires the most recently queued buffer, and sets the
+ * image contents of the target texture to it.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ *
+ * This calls doGLFenceWait to ensure proper synchronization.
+ */
+ status_t updateTexImage();
+
+ /**
+ * releaseTexImage releases the texture acquired in updateTexImage().
+ * This is intended to be used in single buffer mode.
+ *
+ * This call may only be made while the OpenGL ES context to which the
+ * target texture belongs is bound to the calling thread.
+ */
+ status_t releaseTexImage();
+
+ /**
+ * getTransformMatrix retrieves the 4x4 texture coordinate transform matrix
+ * associated with the texture image set by the most recent call to
+ * updateTexImage.
+ *
+ * This transform matrix maps 2D homogeneous texture coordinates of the form
+ * (s, t, 0, 1) with s and t in the inclusive range [0, 1] to the texture
+ * coordinate that should be used to sample that location from the texture.
+ * Sampling the texture outside of the range of this transform is undefined.
+ *
+ * This transform is necessary to compensate for transforms that the stream
+ * content producer may implicitly apply to the content. By forcing users of
+ * a SurfaceTexture to apply this transform we avoid performing an extra
+ * copy of the data that would be needed to hide the transform from the
+ * user.
+ *
+ * The matrix is stored in column-major order so that it may be passed
+ * directly to OpenGL ES via the glLoadMatrixf or glUniformMatrix4fv
+ * functions.
+ */
+ void getTransformMatrix(float mtx[16]);
+
+ /**
+ * Computes the transform matrix documented by getTransformMatrix
+ * from the BufferItem sub parts.
+ */
+ static void computeTransformMatrix(float outTransform[16], const sp<GraphicBuffer>& buf,
+ const Rect& cropRect, uint32_t transform, bool filtering);
+
+ /**
+ * Scale the crop down horizontally or vertically such that it has the
+ * same aspect ratio as the buffer does.
+ */
+ static Rect scaleDownCrop(const Rect& crop, uint32_t bufferWidth, uint32_t bufferHeight);
+
+ /**
+ * getTimestamp retrieves the timestamp associated with the texture image
+ * set by the most recent call to updateTexImage.
+ *
+ * The timestamp is in nanoseconds, and is monotonically increasing. Its
+ * other semantics (zero point, etc) are source-dependent and should be
+ * documented by the source.
+ */
+ int64_t getTimestamp();
+
+ /**
+ * getDataSpace retrieves the DataSpace associated with the texture image
+ * set by the most recent call to updateTexImage.
+ */
+ android_dataspace getCurrentDataSpace();
+
+ /**
+ * getFrameNumber retrieves the frame number associated with the texture
+ * image set by the most recent call to updateTexImage.
+ *
+ * The frame number is an incrementing counter set to 0 at the creation of
+ * the BufferQueue associated with this consumer.
+ */
+ uint64_t getFrameNumber();
+
+ /**
+ * setDefaultBufferSize is used to set the size of buffers returned by
+ * requestBuffers when a with and height of zero is requested.
+ * A call to setDefaultBufferSize() may trigger requestBuffers() to
+ * be called from the client.
+ * The width and height parameters must be no greater than the minimum of
+ * GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+ * An error due to invalid dimensions might not be reported until
+ * updateTexImage() is called.
+ */
+ status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+ /**
+ * setFilteringEnabled sets whether the transform matrix should be computed
+ * for use with bilinear filtering.
+ */
+ void setFilteringEnabled(bool enabled);
+
+ /**
+ * getCurrentTextureTarget returns the texture target of the current
+ * texture as returned by updateTexImage().
+ */
+ uint32_t getCurrentTextureTarget() const;
+
+ /**
+ * getCurrentCrop returns the cropping rectangle of the current buffer.
+ */
+ Rect getCurrentCrop() const;
+
+ /**
+ * getCurrentTransform returns the transform of the current buffer.
+ */
+ uint32_t getCurrentTransform() const;
+
+ /**
+ * getCurrentScalingMode returns the scaling mode of the current buffer.
+ */
+ uint32_t getCurrentScalingMode() const;
+
+ /**
+ * getCurrentFence returns the fence indicating when the current buffer is
+ * ready to be read from.
+ */
+ sp<Fence> getCurrentFence() const;
+
+ /**
+ * getCurrentFence returns the FenceTime indicating when the current
+ * buffer is ready to be read from.
+ */
+ std::shared_ptr<FenceTime> getCurrentFenceTime() const;
+
+ /**
+ * setConsumerUsageBits overrides the ConsumerBase method to OR
+ * DEFAULT_USAGE_FLAGS to usage.
+ */
+ status_t setConsumerUsageBits(uint64_t usage);
+
+ /**
+ * detachFromContext detaches the SurfaceTexture from the calling thread's
+ * current OpenGL ES context. This context must be the same as the context
+ * that was current for previous calls to updateTexImage.
+ *
+ * Detaching a SurfaceTexture from an OpenGL ES context will result in the
+ * deletion of the OpenGL ES texture object into which the images were being
+ * streamed. After a SurfaceTexture has been detached from the OpenGL ES
+ * context calls to updateTexImage will fail returning INVALID_OPERATION
+ * until the SurfaceTexture is attached to a new OpenGL ES context using the
+ * attachToContext method.
+ */
+ status_t detachFromContext();
+
+ /**
+ * attachToContext attaches a SurfaceTexture that is currently in the
+ * 'detached' state to the current OpenGL ES context. A SurfaceTexture is
+ * in the 'detached' state iff detachFromContext has successfully been
+ * called and no calls to attachToContext have succeeded since the last
+ * detachFromContext call. Calls to attachToContext made on a
+ * SurfaceTexture that is not in the 'detached' state will result in an
+ * INVALID_OPERATION error.
+ *
+ * The tex argument specifies the OpenGL ES texture object name in the
+ * new context into which the image contents will be streamed. A successful
+ * call to attachToContext will result in this texture object being bound to
+ * the texture target and populated with the image contents that were
+ * current at the time of the last call to detachFromContext.
+ */
+ status_t attachToContext(uint32_t tex);
+
+ sk_sp<SkImage> dequeueImage(SkMatrix& transformMatrix, android_dataspace& dataSpace,
+ bool* queueEmpty, uirenderer::RenderState& renderState);
+
+ /**
+ * attachToView attaches a SurfaceTexture that is currently in the
+ * 'detached' state to HWUI View system.
+ */
+ void attachToView();
+
+ /**
+ * detachFromView detaches a SurfaceTexture from HWUI View system.
+ */
+ void detachFromView();
+
+protected:
+ /**
+ * abandonLocked overrides the ConsumerBase method to clear
+ * mCurrentTextureImage in addition to the ConsumerBase behavior.
+ */
+ virtual void abandonLocked();
+
+ /**
+ * dumpLocked overrides the ConsumerBase method to dump SurfaceTexture-
+ * specific info in addition to the ConsumerBase behavior.
+ */
+ virtual void dumpLocked(String8& result, const char* prefix) const override;
+
+ /**
+ * acquireBufferLocked overrides the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase behavior.
+ */
+ virtual status_t acquireBufferLocked(BufferItem* item, nsecs_t presentWhen,
+ uint64_t maxFrameNumber = 0) override;
+
+ /**
+ * releaseBufferLocked overrides the ConsumerBase method to update the
+ * mEglSlots array in addition to the ConsumerBase.
+ */
+ virtual status_t releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer,
+ EGLDisplay display, EGLSyncKHR eglFence) override;
+
+ /**
+ * freeBufferLocked frees up the given buffer slot. If the slot has been
+ * initialized this will release the reference to the GraphicBuffer in that
+ * slot and destroy the EGLImage in that slot. Otherwise it has no effect.
+ *
+ * This method must be called with mMutex locked.
+ */
+ virtual void freeBufferLocked(int slotIndex);
+
+ /**
+ * computeCurrentTransformMatrixLocked computes the transform matrix for the
+ * current texture. It uses mCurrentTransform and the current GraphicBuffer
+ * to compute this matrix and stores it in mCurrentTransformMatrix.
+ * mCurrentTextureImage must not be NULL.
+ */
+ void computeCurrentTransformMatrixLocked();
+
+ /**
+ * The default consumer usage flags that SurfaceTexture always sets on its
+ * BufferQueue instance; these will be OR:d with any additional flags passed
+ * from the SurfaceTexture user. In particular, SurfaceTexture will always
+ * consume buffers as hardware textures.
+ */
+ static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
+
+ /**
+ * mCurrentCrop is the crop rectangle that applies to the current texture.
+ * It gets set each time updateTexImage is called.
+ */
+ Rect mCurrentCrop;
+
+ /**
+ * mCurrentTransform is the transform identifier for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ uint32_t mCurrentTransform;
+
+ /**
+ * mCurrentScalingMode is the scaling mode for the current texture. It gets
+ * set each time updateTexImage is called.
+ */
+ uint32_t mCurrentScalingMode;
+
+ /**
+ * mCurrentFence is the fence received from BufferQueue in updateTexImage.
+ */
+ sp<Fence> mCurrentFence;
+
+ /**
+ * The FenceTime wrapper around mCurrentFence.
+ */
+ std::shared_ptr<FenceTime> mCurrentFenceTime{FenceTime::NO_FENCE};
+
+ /**
+ * mCurrentTransformMatrix is the transform matrix for the current texture.
+ * It gets computed by computeTransformMatrix each time updateTexImage is
+ * called.
+ */
+ float mCurrentTransformMatrix[16];
+
+ /**
+ * mCurrentTimestamp is the timestamp for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ int64_t mCurrentTimestamp;
+
+ /**
+ * mCurrentDataSpace is the dataspace for the current texture. It
+ * gets set each time updateTexImage is called.
+ */
+ android_dataspace mCurrentDataSpace;
+
+ /**
+ * mCurrentFrameNumber is the frame counter for the current texture.
+ * It gets set each time updateTexImage is called.
+ */
+ uint64_t mCurrentFrameNumber;
+
+ uint32_t mDefaultWidth, mDefaultHeight;
+
+ /**
+ * mFilteringEnabled indicates whether the transform matrix is computed for
+ * use with bilinear filtering. It defaults to true and is changed by
+ * setFilteringEnabled().
+ */
+ bool mFilteringEnabled;
+
+ /**
+ * mTexName is the name of the OpenGL texture to which streamed images will
+ * be bound when updateTexImage is called. It is set at construction time
+ * and can be changed with a call to attachToContext.
+ */
+ uint32_t mTexName;
+
+ /**
+ * mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync
+ * extension should be used to prevent buffers from being dequeued before
+ * it's safe for them to be written. It gets set at construction time and
+ * never changes.
+ */
+ const bool mUseFenceSync;
+
+ /**
+ * mTexTarget is the GL texture target with which the GL texture object is
+ * associated. It is set in the constructor and never changed. It is
+ * almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android
+ * Browser. In that case it is set to GL_TEXTURE_2D to allow
+ * glCopyTexSubImage to read from the texture. This is a hack to work
+ * around a GL driver limitation on the number of FBO attachments, which the
+ * browser's tile cache exceeds.
+ */
+ const uint32_t mTexTarget;
+
+ /**
+ * mCurrentTexture is the buffer slot index of the buffer that is currently
+ * bound to the OpenGL texture. It is initialized to INVALID_BUFFER_SLOT,
+ * indicating that no buffer slot is currently bound to the texture. Note,
+ * however, that a value of INVALID_BUFFER_SLOT does not necessarily mean
+ * that no buffer is bound to the texture. A call to setBufferCount will
+ * reset mCurrentTexture to INVALID_BUFFER_SLOT.
+ */
+ int mCurrentTexture;
+
+ enum class OpMode { detached, attachedToView, attachedToGL };
+ /**
+ * mOpMode indicates whether the SurfaceTexture is currently attached to
+ * an OpenGL ES context or the HWUI view system. For legacy reasons, this is initialized to,
+ * "attachedToGL" indicating that the SurfaceTexture is considered to be attached to
+ * whatever GL context is current at the time of the first updateTexImage call.
+ * It is set to "detached" by detachFromContext, and then set to "attachedToGL" again by
+ * attachToContext.
+ * attachToView/detachFromView are used to attach/detach from HWUI view system.
+ */
+ OpMode mOpMode;
+
+ /**
+ * mEGLConsumer has SurfaceTexture logic used when attached to GL context.
+ */
+ EGLConsumer mEGLConsumer;
+
+ /**
+ * mImageConsumer has SurfaceTexture logic used when attached to HWUI view system.
+ */
+ ImageConsumer mImageConsumer;
+
+ friend class ImageConsumer;
+ friend class EGLConsumer;
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp
index 5b36154..d2d37dc 100644
--- a/libs/hwui/tests/common/LeakChecker.cpp
+++ b/libs/hwui/tests/common/LeakChecker.cpp
@@ -16,7 +16,6 @@
#include "LeakChecker.h"
-#include "Caches.h"
#include "TestUtils.h"
#include <memunreachable/memunreachable.h>
@@ -71,9 +70,6 @@
// thread-local caches so some leaks will not be properly tagged as leaks
UnreachableMemoryInfo rtMemInfo;
TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) {
- if (Caches::hasInstance()) {
- Caches::getInstance().tasks.stop();
- }
// Check for leaks
if (!GetUnreachableMemory(rtMemInfo)) {
cerr << "Failed to get unreachable memory!" << endl;
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 6958634..66b9b85 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -67,16 +67,14 @@
renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
const SkMatrix& transform) {
sp<DeferredLayerUpdater> layerUpdater = createTextureLayerUpdater(renderThread);
- layerUpdater->backingLayer()->getTransform().load(transform);
+ layerUpdater->backingLayer()->getTransform() = transform;
layerUpdater->setSize(width, height);
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN);
- if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
- static_cast<GlLayer*>(layerUpdater->backingLayer())
- ->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
- }
+ SkMatrix identity;
+ identity.setIdentity();
+ layerUpdater->updateLayer(true, identity, HAL_DATASPACE_UNKNOWN, nullptr);
return layerUpdater;
}
@@ -117,7 +115,6 @@
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
renderThread.vulkanManager().destroy();
} else {
- renderThread.renderState().flush(Caches::FlushMode::Full);
renderThread.destroyGlContext();
}
}
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 743f809..0e6582c 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -18,7 +18,6 @@
#include <DeviceInfo.h>
#include <DisplayList.h>
-#include <GlLayer.h>
#include <Matrix.h>
#include <Properties.h>
#include <Rect.h>
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index f29830f..6c8775b 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -15,12 +15,13 @@
*/
#include "DeferredLayerUpdater.h"
-#include "GlLayer.h"
#include "Properties.h"
#include "tests/common/TestUtils.h"
#include <gtest/gtest.h>
+#include <SkBitmap.h>
+#include <SkImage.h>
using namespace android;
using namespace android::uirenderer;
@@ -31,10 +32,6 @@
layerUpdater->setBlend(true);
// updates are deferred so the backing layer should still be in its default state
- if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
- GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
- EXPECT_EQ((uint32_t)GL_NONE, glLayer->getRenderTarget());
- }
EXPECT_EQ(0u, layerUpdater->backingLayer()->getWidth());
EXPECT_EQ(0u, layerUpdater->backingLayer()->getHeight());
EXPECT_FALSE(layerUpdater->backingLayer()->getForceFilter());
@@ -42,19 +39,13 @@
EXPECT_EQ(Matrix4::identity(), layerUpdater->backingLayer()->getTexTransform());
// push the deferred updates to the layer
- Matrix4 scaledMatrix;
- scaledMatrix.loadScale(0.5, 0.5, 0.0);
- layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN);
- if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
- GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
- glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
- }
+ SkMatrix scaledMatrix = SkMatrix::MakeScale(0.5, 0.5);
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(16, 16);
+ sk_sp<SkImage> layerImage = SkImage::MakeFromBitmap(bitmap);
+ layerUpdater->updateLayer(true, scaledMatrix, HAL_DATASPACE_UNKNOWN, layerImage);
// the backing layer should now have all the properties applied.
- if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
- GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
- EXPECT_EQ((uint32_t)GL_TEXTURE_EXTERNAL_OES, glLayer->getRenderTarget());
- }
EXPECT_EQ(100u, layerUpdater->backingLayer()->getWidth());
EXPECT_EQ(100u, layerUpdater->backingLayer()->getHeight());
EXPECT_TRUE(layerUpdater->backingLayer()->getForceFilter());
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 43080a9..1433aa0 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -48,11 +48,18 @@
*/
static void terminate(ShaderCache& cache, bool saveContent) {
std::lock_guard<std::mutex> lock(cache.mMutex);
- if (cache.mInitialized && cache.mBlobCache && saveContent) {
- cache.mBlobCache->writeToFile();
- }
+ cache.mSavePending = saveContent;
+ cache.saveToDiskLocked();
cache.mBlobCache = NULL;
}
+
+ /**
+ *
+ */
+ template <typename T>
+ static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
+ return cache.validateCache(hash.data(), hash.size() * sizeof(T));
+ }
};
} /* namespace skiapipeline */
@@ -75,26 +82,39 @@
return false;
}
-bool checkShader(const sk_sp<SkData>& shader, const char* program) {
- sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
- return shader->size() == shader2->size()
- && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+inline bool
+checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
+ return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size()
+ && 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
}
-bool checkShader(const sk_sp<SkData>& shader, std::vector<char>& program) {
- sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size());
- return shader->size() == shader2->size()
- && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+inline bool
+checkShader(const sk_sp<SkData>& shader, const char* program) {
+ sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
+ return checkShader(shader, shader2);
+}
+
+template <typename T>
+bool checkShader(const sk_sp<SkData>& shader, std::vector<T>& program) {
+ sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size() * sizeof(T));
+ return checkShader(shader, shader2);
}
void setShader(sk_sp<SkData>& shader, const char* program) {
shader = SkData::MakeWithCString(program);
}
-void setShader(sk_sp<SkData>& shader, std::vector<char>& program) {
- shader = SkData::MakeWithCopy(program.data(), program.size());
+template <typename T>
+void setShader(sk_sp<SkData>& shader, std::vector<T>& buffer) {
+ shader = SkData::MakeWithCopy(buffer.data(), buffer.size() * sizeof(T));
}
+template <typename T>
+void genRandomData(std::vector<T>& buffer) {
+ for (auto& data : buffer) {
+ data = T(std::rand());
+ }
+}
#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
@@ -110,6 +130,7 @@
//remove any test files from previous test run
int deleteFile = remove(cacheFile1.c_str());
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+ std::srand(0);
//read the cache from a file that does not exist
ShaderCache::get().setFilename(cacheFile1.c_str());
@@ -158,10 +179,8 @@
//write and read big data chunk (50K)
size_t dataSize = 50*1024;
- std::vector<char> dataBuffer(dataSize);
- for (size_t i = 0; i < dataSize; i++) {
- dataBuffer[0] = dataSize % 256;
- }
+ std::vector<uint8_t> dataBuffer(dataSize);
+ genRandomData(dataBuffer);
setShader(inVS, dataBuffer);
ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
@@ -173,4 +192,96 @@
remove(cacheFile1.c_str());
}
+TEST(ShaderCacheTest, testCacheValidation) {
+ if (!folderExist(getExternalStorageFolder())) {
+ //don't run the test if external storage folder is not available
+ return;
+ }
+ std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+ std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
+
+ //remove any test files from previous test run
+ int deleteFile = remove(cacheFile1.c_str());
+ ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+ std::srand(0);
+
+ //generate identity and read the cache from a file that does not exist
+ ShaderCache::get().setFilename(cacheFile1.c_str());
+ ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+ std::vector<uint8_t> identity(1024);
+ genRandomData(identity);
+ ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
+ sizeof(decltype(identity)::value_type));
+
+ // generate random content in cache and store to disk
+ constexpr size_t numBlob(10);
+ constexpr size_t keySize(1024);
+ constexpr size_t dataSize(50 * 1024);
+
+ std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob);
+ for (auto& blob : blobVec) {
+ std::vector<uint8_t> keyBuffer(keySize);
+ std::vector<uint8_t> dataBuffer(dataSize);
+ genRandomData(keyBuffer);
+ genRandomData(dataBuffer);
+
+ sk_sp<SkData> key, data;
+ setShader(key, keyBuffer);
+ setShader(data, dataBuffer);
+
+ blob = std::make_pair(key, data);
+ ShaderCache::get().store(*key.get(), *data.get());
+ }
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+
+ // change to a file that does not exist and verify validation fails
+ ShaderCache::get().setFilename(cacheFile2.c_str());
+ ShaderCache::get().initShaderDiskCache();
+ ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+
+ // restore the original file and verify validation succeeds
+ ShaderCache::get().setFilename(cacheFile1.c_str());
+ ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
+ sizeof(decltype(identity)::value_type));
+ ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ for (const auto& blob : blobVec) {
+ auto outVS = ShaderCache::get().load(*blob.first.get());
+ ASSERT_TRUE( checkShader(outVS, blob.second) );
+ }
+
+ // generate error identity and verify load fails
+ ShaderCache::get().initShaderDiskCache(identity.data(), -1);
+ for (const auto& blob : blobVec) {
+ ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ }
+ ShaderCache::get().initShaderDiskCache(nullptr, identity.size() *
+ sizeof(decltype(identity)::value_type));
+ for (const auto& blob : blobVec) {
+ ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ }
+
+ // verify the cache validation again after load fails
+ ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
+ sizeof(decltype(identity)::value_type));
+ ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
+ for (const auto& blob : blobVec) {
+ auto outVS = ShaderCache::get().load(*blob.first.get());
+ ASSERT_TRUE( checkShader(outVS, blob.second) );
+ }
+
+ // generate another identity and verify load fails
+ for (auto& data : identity) {
+ data += std::rand();
+ }
+ ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
+ sizeof(decltype(identity)::value_type));
+ for (const auto& blob : blobVec) {
+ ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
+ }
+
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+ remove(cacheFile1.c_str());
+}
+
} // namespace
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 9e6d9a8..aecceb3 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -17,12 +17,13 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "Caches.h"
#include "debug/GlesDriver.h"
#include "debug/NullGlesDriver.h"
#include "hwui/Typeface.h"
#include "Properties.h"
#include "tests/common/LeakChecker.h"
+#include "thread/TaskProcessor.h"
+#include "thread/Task.h"
#include "thread/TaskManager.h"
#include <signal.h>
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index f8e8a0a..ebf2343 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -16,6 +16,7 @@
#ifndef PAINT_UTILS_H
#define PAINT_UTILS_H
+#include <GLES2/gl2.h>
#include <utils/Blur.h>
#include <SkColorFilter.h>
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
deleted file mode 100644
index 8ac6e1f..0000000
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "TestWindowContext.h"
-
-#include "AnimationContext.h"
-#include "IContextFactory.h"
-#include "RenderNode.h"
-#include "SkTypes.h"
-#include "gui/BufferQueue.h"
-#include "gui/CpuConsumer.h"
-#include "gui/IGraphicBufferConsumer.h"
-#include "gui/IGraphicBufferProducer.h"
-#include "gui/Surface.h"
-#include "hwui/Canvas.h"
-#include "renderthread/RenderProxy.h"
-
-#include <cutils/memory.h>
-
-namespace {
-
-/**
- * Helper class for setting up android::uirenderer::renderthread::RenderProxy.
- */
-class ContextFactory : public android::uirenderer::IContextFactory {
-public:
- android::uirenderer::AnimationContext* createAnimationContext(
- android::uirenderer::renderthread::TimeLord& clock) override {
- return new android::uirenderer::AnimationContext(clock);
- }
-};
-
-} // anonymous namespace
-
-namespace android {
-namespace uirenderer {
-
-/**
- Android strong pointers (android::sp) can't hold forward-declared classes,
- so we have to use pointer-to-implementation here if we want to hide the
- details from our non-framework users.
-*/
-
-class TestWindowContext::TestWindowData {
-public:
- explicit TestWindowData(SkISize size) : mSize(size) {
- android::BufferQueue::createBufferQueue(&mProducer, &mConsumer);
- mCpuConsumer = new android::CpuConsumer(mConsumer, 1);
- mCpuConsumer->setName(android::String8("TestWindowContext"));
- mCpuConsumer->setDefaultBufferSize(mSize.width(), mSize.height());
- mAndroidSurface = new android::Surface(mProducer);
- native_window_set_buffers_dimensions(mAndroidSurface.get(), mSize.width(), mSize.height());
- native_window_set_buffers_format(mAndroidSurface.get(), android::PIXEL_FORMAT_RGBA_8888);
- native_window_set_usage(mAndroidSurface.get(), GRALLOC_USAGE_SW_READ_OFTEN |
- GRALLOC_USAGE_SW_WRITE_NEVER |
- GRALLOC_USAGE_HW_RENDER);
- mRootNode.reset(new android::uirenderer::RenderNode());
- mRootNode->incStrong(nullptr);
- mRootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, mSize.width(),
- mSize.height());
- mRootNode->mutateStagingProperties().setClipToBounds(false);
- mRootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC);
- ContextFactory factory;
- mProxy.reset(new android::uirenderer::renderthread::RenderProxy(false, mRootNode.get(),
- &factory));
- mProxy->loadSystemProperties();
- mProxy->initialize(mAndroidSurface.get());
- float lightX = mSize.width() / 2.0f;
- android::uirenderer::Vector3 lightVector{lightX, -200.0f, 800.0f};
- mProxy->setup(800.0f, 255 * 0.075f, 255 * 0.15f);
- mProxy->setLightCenter(lightVector);
- mCanvas.reset(Canvas::create_recording_canvas(mSize.width(), mSize.height(), mRootNode.get()));
- }
-
- SkCanvas* prepareToDraw() {
- // mCanvas->reset(mSize.width(), mSize.height());
- mCanvas->clipRect(0, 0, mSize.width(), mSize.height(), SkClipOp::kReplace_deprecated);
- return mCanvas->asSkCanvas();
- }
-
- void finishDrawing() {
- mRootNode->setStagingDisplayList(mCanvas->finishRecording());
- mProxy->syncAndDrawFrame();
- // Surprisingly, calling mProxy->fence() here appears to make no difference to
- // the timings we record.
- }
-
- void fence() { mProxy->fence(); }
-
- bool capturePixels(SkBitmap* bmp) {
- sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
- SkImageInfo destinationConfig =
- SkImageInfo::Make(mSize.width(), mSize.height(), kRGBA_8888_SkColorType,
- kPremul_SkAlphaType, colorSpace);
- bmp->allocPixels(destinationConfig);
- android_memset32((uint32_t*)bmp->getPixels(), SK_ColorRED,
- mSize.width() * mSize.height() * 4);
-
- android::CpuConsumer::LockedBuffer nativeBuffer;
- android::status_t retval = mCpuConsumer->lockNextBuffer(&nativeBuffer);
- if (retval == android::BAD_VALUE) {
- SkDebugf("write_canvas_png() got no buffer; returning transparent");
- // No buffer ready to read - commonly triggered by dm sending us
- // a no-op source, or calling code that doesn't do anything on this
- // backend.
- bmp->eraseColor(SK_ColorTRANSPARENT);
- return false;
- } else if (retval) {
- SkDebugf("Failed to lock buffer to read pixels: %d.", retval);
- return false;
- }
-
- // Move the pixels into the destination SkBitmap
-
- LOG_ALWAYS_FATAL_IF(nativeBuffer.format != android::PIXEL_FORMAT_RGBA_8888,
- "Native buffer not RGBA!");
- SkImageInfo nativeConfig = SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height,
- kRGBA_8888_SkColorType, kPremul_SkAlphaType);
-
- // Android stride is in pixels, Skia stride is in bytes
- SkBitmap nativeWrapper;
- bool success = nativeWrapper.installPixels(nativeConfig, nativeBuffer.data,
- nativeBuffer.stride * 4);
- if (!success) {
- SkDebugf("Failed to wrap HWUI buffer in a SkBitmap");
- return false;
- }
-
- LOG_ALWAYS_FATAL_IF(bmp->colorType() != kRGBA_8888_SkColorType,
- "Destination buffer not RGBA!");
- success = nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0,
- 0);
- if (!success) {
- SkDebugf("Failed to extract pixels from HWUI buffer");
- return false;
- }
-
- mCpuConsumer->unlockBuffer(nativeBuffer);
-
- return true;
- }
-
-private:
- std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
- std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
- std::unique_ptr<android::Canvas> mCanvas;
- android::sp<android::IGraphicBufferProducer> mProducer;
- android::sp<android::IGraphicBufferConsumer> mConsumer;
- android::sp<android::CpuConsumer> mCpuConsumer;
- android::sp<android::Surface> mAndroidSurface;
- SkISize mSize;
-};
-
-TestWindowContext::TestWindowContext() : mData(nullptr) {}
-
-TestWindowContext::~TestWindowContext() {
- delete mData;
-}
-
-void TestWindowContext::initialize(int width, int height) {
- mData = new TestWindowData(SkISize::Make(width, height));
-}
-
-SkCanvas* TestWindowContext::prepareToDraw() {
- return mData ? mData->prepareToDraw() : nullptr;
-}
-
-void TestWindowContext::finishDrawing() {
- if (mData) {
- mData->finishDrawing();
- }
-}
-
-void TestWindowContext::fence() {
- if (mData) {
- mData->fence();
- }
-}
-
-bool TestWindowContext::capturePixels(SkBitmap* bmp) {
- return mData ? mData->capturePixels(bmp) : false;
-}
-
-} // namespace uirenderer
-} // namespace android
diff --git a/libs/hwui/utils/TestWindowContext.h b/libs/hwui/utils/TestWindowContext.h
deleted file mode 100644
index 17ad1e3..0000000
--- a/libs/hwui/utils/TestWindowContext.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef TESTWINDOWCONTEXT_H_
-#define TESTWINDOWCONTEXT_H_
-
-#include <cutils/compiler.h>
-
-class SkBitmap;
-class SkCanvas;
-
-namespace android {
-
-namespace uirenderer {
-
-/**
- Wraps all libui/libgui classes and types that external tests depend on,
- exposing only primitive Skia types.
-*/
-
-class ANDROID_API TestWindowContext {
-public:
- TestWindowContext();
- ~TestWindowContext();
-
- /// We need to know the size of the window.
- void initialize(int width, int height);
-
- /// Returns a canvas to draw into; NULL if not yet initialize()d.
- SkCanvas* prepareToDraw();
-
- /// Flushes all drawing commands to HWUI; no-op if not yet initialize()d.
- void finishDrawing();
-
- /// Blocks until HWUI has processed all pending drawing commands;
- /// no-op if not yet initialize()d.
- void fence();
-
- /// Returns false if not yet initialize()d.
- bool capturePixels(SkBitmap* bmp);
-
-private:
- /// Hidden implementation.
- class TestWindowData;
-
- TestWindowData* mData;
-};
-
-} // namespace uirenderer
-} // namespace android
-
-#endif // TESTWINDOWCONTEXT_H_
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 67682a0..c5a6ec5 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -36,9 +36,8 @@
cflags: [
"-Wall",
+ "-Wextra",
"-Werror",
- "-Wunused",
- "-Wunreachable-code",
],
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 4794f3d..eb3469e 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -28,7 +28,6 @@
#include <utils/BitSet.h>
#include <utils/RefBase.h>
#include <utils/Looper.h>
-#include <utils/String8.h>
#include <gui/DisplayEventReceiver.h>
namespace android {
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 173cd50..eb2bc98 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -23,14 +23,10 @@
#include <utils/String8.h>
#include <gui/Surface.h>
-// ToDo: Fix code to be warning free
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkColor.h>
#include <SkPaint.h>
-#pragma GCC diagnostic pop
#include <android/native_window.h>
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index b5d9431..6d10c2d 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -2378,4 +2378,17 @@
throw new IllegalArgumentException("invalid geofence: " + fence);
}
}
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public String getNetworkProviderPackage() {
+ try {
+ return mService.getNetworkProviderPackage();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
}
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index e8bc622..0476222 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1035,7 +1035,7 @@
return fromGetVolumeControlStream ?
AudioSystem.STREAM_SYSTEM : AudioSystem.STREAM_SYSTEM_ENFORCED;
}
- if ((aa.getFlags() & FLAG_SCO) == FLAG_SCO) {
+ if ((aa.getAllFlags() & FLAG_SCO) == FLAG_SCO) {
return fromGetVolumeControlStream ?
AudioSystem.STREAM_VOICE_CALL : AudioSystem.STREAM_BLUETOOTH_SCO;
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 39f3b31..e8fe9fe 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -882,6 +882,10 @@
public static native int listAudioPatches(ArrayList<AudioPatch> patches, int[] generation);
public static native int setAudioPortConfig(AudioPortConfig config);
+ public static native int startAudioSource(AudioPortConfig config,
+ AudioAttributes audioAttributes);
+ public static native int stopAudioSource(int handle);
+
// declare this instance as having a dynamic policy callback handler
private static native final void native_register_dynamic_policy_callback();
// declare this instance as having a recording configuration update callback handler
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index aa78b0d..89827bc 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -773,22 +773,6 @@
}
/**
- * Invoke a generic method on the native player using opaque
- * parcels for the request and reply. Both payloads' format is a
- * convention between the java caller and the native player.
- * Must be called after setDataSource to make sure a native player
- * exists. On failure, a RuntimeException is thrown.
- *
- * @param request Parcel with the data for the extension. The
- * caller must use {@link #newRequest()} to get one.
- *
- * @param reply Output parcel with the data returned by the
- * native player.
- * {@hide}
- */
- public void invoke(Parcel request, Parcel reply) { }
-
- /**
* Insert a task in the command queue to help the client to identify whether a batch
* of commands has been finished. When this command is processed, a notification
* {@code EventCallback.onCommandLabelReached} will be fired with the
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index c06b97b..6ae4d40 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -25,6 +25,10 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.graphics.SurfaceTexture;
+import android.graphics.Rect;
+import android.media.MediaPlayer2Proto;
+import android.media.MediaPlayer2Proto.PlayerMessage;
+import android.media.MediaPlayer2Proto.Value;
import android.media.SubtitleController.Anchor;
import android.media.SubtitleTrack.RenderingWidget;
import android.net.Uri;
@@ -49,6 +53,7 @@
import android.view.SurfaceHolder;
import android.widget.VideoView;
+import com.android.framework.protobuf.InvalidProtocolBufferException;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
@@ -72,6 +77,7 @@
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -555,28 +561,30 @@
}
/**
- * Invoke a generic method on the native player using opaque
- * parcels for the request and reply. Both payloads' format is a
+ * Invoke a generic method on the native player using opaque protocol
+ * buffer message for the request and reply. Both payloads' format is a
* convention between the java caller and the native player.
- * Must be called after setDataSource or setPlaylist to make sure a native player
- * exists. On failure, a RuntimeException is thrown.
*
- * @param request Parcel with the data for the extension. The
+ * @param request PlayerMessage for the extension. The
* caller must use {@link #newRequest()} to get one.
*
- * @param reply Output parcel with the data returned by the
+ * @return PlayerMessage with the data returned by the
* native player.
- * {@hide}
*/
- @Override
- public void invoke(Parcel request, Parcel reply) {
- int retcode = native_invoke(request, reply);
- reply.setDataPosition(0);
- if (retcode != 0) {
- throw new RuntimeException("failure code: " + retcode);
+ private PlayerMessage invoke(PlayerMessage msg) {
+ byte[] ret = _invoke(msg.toByteArray());
+ if (ret == null) {
+ return null;
+ }
+ try {
+ return PlayerMessage.parseFrom(ret);
+ } catch (InvalidProtocolBufferException e) {
+ return null;
}
}
+ private native byte[] _invoke(byte[] request);
+
@Override
public void notifyWhenCommandLabelReached(Object label) {
addTask(new Task(CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED, false) {
@@ -683,16 +691,12 @@
final String msg = "Scaling mode " + mode + " is not supported";
throw new IllegalArgumentException(msg);
}
- Parcel request = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- try {
- request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE);
- request.writeInt(mode);
- invoke(request, reply);
- } finally {
- request.recycle();
- reply.recycle();
- }
+ PlayerMessage request = PlayerMessage.newBuilder()
+ .addValues(Value.newBuilder()
+ .setInt32Value(INVOKE_ID_SET_VIDEO_SCALE_MODE))
+ .addValues(Value.newBuilder().setInt32Value(mode))
+ .build();
+ invoke(request);
}
});
}
@@ -1799,14 +1803,6 @@
private native void _setAuxEffectSendLevel(float level);
/*
- * @param request Parcel destinated to the media player.
- * @param reply[out] Parcel that will contain the reply.
- * @return The status code.
- */
- private native final int native_invoke(Parcel request, Parcel reply);
-
-
- /*
* @param update_only If true fetch only the set of metadata that have
* changed since the last invocation of getMetadata.
* The set is built using the unfiltered
@@ -1886,18 +1882,18 @@
final int mTrackType;
final MediaFormat mFormat;
- TrackInfoImpl(Parcel in) {
- mTrackType = in.readInt();
- // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat
+ TrackInfoImpl(Iterator<Value> in) {
+ mTrackType = in.next().getInt32Value();
+ // TODO: build the full MediaFormat; currently we are using createSubtitleFormat
// even for audio/video tracks, meaning we only set the mime and language.
- String mime = in.readString();
- String language = in.readString();
+ String mime = in.next().getStringValue();
+ String language = in.next().getStringValue();
mFormat = MediaFormat.createSubtitleFormat(mime, language);
if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
- mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt());
- mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt());
- mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt());
+ mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.next().getInt32Value());
+ mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.next().getInt32Value());
+ mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.next().getInt32Value());
}
}
@@ -1952,23 +1948,6 @@
out.append("}");
return out.toString();
}
-
- /**
- * Used to read a TrackInfoImpl from a Parcel.
- */
- /* package private */ static final Parcelable.Creator<TrackInfoImpl> CREATOR
- = new Parcelable.Creator<TrackInfoImpl>() {
- @Override
- public TrackInfoImpl createFromParcel(Parcel in) {
- return new TrackInfoImpl(in);
- }
-
- @Override
- public TrackInfoImpl[] newArray(int size) {
- return new TrackInfoImpl[size];
- }
- };
-
};
// We would like domain specific classes with more informative names than the `first` and `second`
@@ -2010,17 +1989,23 @@
}
private TrackInfoImpl[] getInbandTrackInfoImpl() throws IllegalStateException {
- Parcel request = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- try {
- request.writeInt(INVOKE_ID_GET_TRACK_INFO);
- invoke(request, reply);
- TrackInfoImpl trackInfo[] = reply.createTypedArray(TrackInfoImpl.CREATOR);
- return trackInfo;
- } finally {
- request.recycle();
- reply.recycle();
+ PlayerMessage request = PlayerMessage.newBuilder()
+ .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_TRACK_INFO))
+ .build();
+ PlayerMessage response = invoke(request);
+ if (response == null) {
+ return null;
}
+ Iterator<Value> in = response.getValuesList().iterator();
+ int size = in.next().getInt32Value();
+ if (size == 0) {
+ return null;
+ }
+ TrackInfoImpl trackInfo[] = new TrackInfoImpl[size];
+ for (int i = 0; i < size; ++i) {
+ trackInfo[i] = new TrackInfoImpl(in);
+ }
+ return trackInfo;
}
/*
@@ -2481,26 +2466,24 @@
}
}
- Parcel request = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- try {
- request.writeInt(INVOKE_ID_GET_SELECTED_TRACK);
- request.writeInt(trackType);
- invoke(request, reply);
- int inbandTrackIndex = reply.readInt();
- synchronized (mIndexTrackPairs) {
- for (int i = 0; i < mIndexTrackPairs.size(); i++) {
- Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
- if (p.first != null && p.first == inbandTrackIndex) {
- return i;
- }
+ PlayerMessage request = PlayerMessage.newBuilder()
+ .addValues(Value.newBuilder().setInt32Value(INVOKE_ID_GET_SELECTED_TRACK))
+ .addValues(Value.newBuilder().setInt32Value(trackType))
+ .build();
+ PlayerMessage response = invoke(request);
+ if (response == null) {
+ return -1;
+ }
+ int inbandTrackIndex = response.getValues(0).getInt32Value();
+ synchronized (mIndexTrackPairs) {
+ for (int i = 0; i < mIndexTrackPairs.size(); i++) {
+ Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
+ if (p.first != null && p.first == inbandTrackIndex) {
+ return i;
}
}
- return -1;
- } finally {
- request.recycle();
- reply.recycle();
}
+ return -1;
}
/**
@@ -2617,16 +2600,12 @@
private void selectOrDeselectInbandTrack(int index, boolean select)
throws IllegalStateException {
- Parcel request = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- try {
- request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK);
- request.writeInt(index);
- invoke(request, reply);
- } finally {
- request.recycle();
- reply.recycle();
- }
+ PlayerMessage request = PlayerMessage.newBuilder()
+ .addValues(Value.newBuilder().setInt32Value(
+ select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK))
+ .addValues(Value.newBuilder().setInt32Value(index))
+ .build();
+ invoke(request);
}
// Have to declare protected for finalize() since it is protected
@@ -2791,8 +2770,8 @@
{
if (msg.obj == null) {
Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
- } else if (msg.obj instanceof Parcel) {
- // The parcel was parsed already in postEventFromNative
+ } else if (msg.obj instanceof byte[]) {
+ // The PlayerMessage was parsed already in postEventFromNative
final DrmInfoImpl drmInfo;
synchronized (mDrmLock) {
@@ -2935,20 +2914,7 @@
case MEDIA_INFO:
{
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, what, extra));
- }
- }
-
switch (msg.arg1) {
- case MEDIA_INFO_DATA_SOURCE_START:
- if (isCurrentSrcId) {
- prepareNextDataSource();
- }
- break;
-
case MEDIA_INFO_VIDEO_TRACK_LAGGING:
Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
break;
@@ -2980,6 +2946,20 @@
}
break;
}
+
+ synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ mMediaPlayer, dsd, what, extra));
+ }
+ }
+
+ if (msg.arg1 == MEDIA_INFO_DATA_SOURCE_START) {
+ if (isCurrentSrcId) {
+ prepareNextDataSource();
+ }
+ }
+
// No real default action so far.
return;
}
@@ -2996,10 +2976,15 @@
case MEDIA_TIMED_TEXT:
{
final TimedText text;
- if (msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel)msg.obj;
- text = new TimedText(parcel);
- parcel.recycle();
+ if (msg.obj instanceof byte[]) {
+ PlayerMessage playerMsg;
+ try {
+ playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
+ } catch (InvalidProtocolBufferException e) {
+ Log.w(TAG, "Failed to parse timed text.", e);
+ return;
+ }
+ text = TimedTextUtil.parsePlayerMessage(playerMsg);
} else {
text = null;
}
@@ -3015,10 +3000,20 @@
case MEDIA_SUBTITLE_DATA:
{
- if (msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel) msg.obj;
- SubtitleData data = new SubtitleData(parcel);
- parcel.recycle();
+ if (msg.obj instanceof byte[]) {
+ PlayerMessage playerMsg;
+ try {
+ playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
+ } catch (InvalidProtocolBufferException e) {
+ Log.w(TAG, "Failed to parse subtitle data.", e);
+ return;
+ }
+ Iterator<Value> in = playerMsg.getValuesList().iterator();
+ SubtitleData data = new SubtitleData(
+ in.next().getInt32Value(), // trackIndex
+ in.next().getInt64Value(), // startTimeUs
+ in.next().getInt64Value(), // durationUs
+ in.next().getBytesValue().toByteArray()); // data
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onSubtitleData(
@@ -3032,10 +3027,18 @@
case MEDIA_META_DATA:
{
final TimedMetaData data;
- if (msg.obj instanceof Parcel) {
- Parcel parcel = (Parcel) msg.obj;
- data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
- parcel.recycle();
+ if (msg.obj instanceof byte[]) {
+ PlayerMessage playerMsg;
+ try {
+ playerMsg = PlayerMessage.parseFrom((byte[]) msg.obj);
+ } catch (InvalidProtocolBufferException e) {
+ Log.w(TAG, "Failed to parse timed meta data.", e);
+ return;
+ }
+ Iterator<Value> in = playerMsg.getValuesList().iterator();
+ data = new TimedMetaData(
+ in.next().getInt64Value(), // timestampUs
+ in.next().getBytesValue().toByteArray()); // metaData
} else {
data = null;
}
@@ -3083,7 +3086,7 @@
* the cookie passed to native_setup().)
*/
private static void postEventFromNative(Object mediaplayer2_ref, long srcId,
- int what, int arg1, int arg2, Object obj)
+ int what, int arg1, int arg2, byte[] obj)
{
final MediaPlayer2Impl mp = (MediaPlayer2Impl)((WeakReference)mediaplayer2_ref).get();
if (mp == null) {
@@ -3097,9 +3100,15 @@
// notification looper so its handleMessage might process the event after prepare()
// has returned.
Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
- if (obj instanceof Parcel) {
- Parcel parcel = (Parcel)obj;
- DrmInfoImpl drmInfo = new DrmInfoImpl(parcel);
+ if (obj != null) {
+ PlayerMessage playerMsg;
+ try {
+ playerMsg = PlayerMessage.parseFrom(obj);
+ } catch (InvalidProtocolBufferException e) {
+ Log.w(TAG, "MEDIA_DRM_INFO failed to parse msg.obj " + obj);
+ break;
+ }
+ DrmInfoImpl drmInfo = new DrmInfoImpl(playerMsg);
synchronized (mp.mDrmLock) {
mp.mDrmInfoImpl = drmInfo;
}
@@ -3766,22 +3775,21 @@
supportedSchemes = SupportedSchemes;
}
- private DrmInfoImpl(Parcel parcel) {
- Log.v(TAG, "DrmInfoImpl(" + parcel + ") size " + parcel.dataSize());
+ private DrmInfoImpl(PlayerMessage msg) {
+ Log.v(TAG, "DrmInfoImpl(" + msg + ")");
- int psshsize = parcel.readInt();
- byte[] pssh = new byte[psshsize];
- parcel.readByteArray(pssh);
+ Iterator<Value> in = msg.getValuesList().iterator();
+ byte[] pssh = in.next().getBytesValue().toByteArray();
Log.v(TAG, "DrmInfoImpl() PSSH: " + arrToHex(pssh));
- mapPssh = parsePSSH(pssh, psshsize);
+ mapPssh = parsePSSH(pssh, pssh.length);
Log.v(TAG, "DrmInfoImpl() PSSH: " + mapPssh);
- int supportedDRMsCount = parcel.readInt();
+ int supportedDRMsCount = in.next().getInt32Value();
supportedSchemes = new UUID[supportedDRMsCount];
for (int i = 0; i < supportedDRMsCount; i++) {
byte[] uuid = new byte[16];
- parcel.readByteArray(uuid);
+ in.next().getBytesValue().copyTo(uuid, uuid.length);
supportedSchemes[i] = bytesToUUID(uuid);
@@ -3789,7 +3797,7 @@
supportedSchemes[i]);
}
- Log.v(TAG, "DrmInfoImpl() Parcel psshsize: " + psshsize +
+ Log.v(TAG, "DrmInfoImpl() Parcel psshsize: " + pssh.length +
" supportedDRMsCount: " + supportedDRMsCount);
}
@@ -4269,6 +4277,60 @@
mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
}
+ private static class TimedTextUtil {
+ // These keys must be in sync with the keys in TextDescription2.h
+ private static final int KEY_START_TIME = 7; // int
+ private static final int KEY_STRUCT_TEXT_POS = 14; // TextPos
+ private static final int KEY_STRUCT_TEXT = 16; // Text
+ private static final int KEY_GLOBAL_SETTING = 101;
+ private static final int KEY_LOCAL_SETTING = 102;
+
+ private static TimedText parsePlayerMessage(PlayerMessage playerMsg) {
+ if (playerMsg.getValuesCount() == 0) {
+ return null;
+ }
+
+ String textChars = null;
+ Rect textBounds = null;
+ Iterator<Value> in = playerMsg.getValuesList().iterator();
+ int type = in.next().getInt32Value();
+ if (type == KEY_LOCAL_SETTING) {
+ type = in.next().getInt32Value();
+ if (type != KEY_START_TIME) {
+ return null;
+ }
+ int startTimeMs = in.next().getInt32Value();
+
+ type = in.next().getInt32Value();
+ if (type != KEY_STRUCT_TEXT) {
+ return null;
+ }
+
+ byte[] text = in.next().getBytesValue().toByteArray();
+ if (text == null || text.length == 0) {
+ textChars = null;
+ } else {
+ textChars = new String(text);
+ }
+
+ } else if (type != KEY_GLOBAL_SETTING) {
+ Log.w(TAG, "Invalid timed text key found: " + type);
+ return null;
+ }
+ if (in.hasNext()) {
+ type = in.next().getInt32Value();
+ if (type == KEY_STRUCT_TEXT_POS) {
+ int top = in.next().getInt32Value();
+ int left = in.next().getInt32Value();
+ int bottom = in.next().getInt32Value();
+ int right = in.next().getInt32Value();
+ textBounds = new Rect(left, top, right, bottom);
+ }
+ }
+ return new TimedText(textChars, textBounds);
+ }
+ }
+
/** @hide */
static class TimeProvider implements MediaTimeProvider {
private static final String TAG = "MTP";
diff --git a/media/java/android/media/SubtitleData.java b/media/java/android/media/SubtitleData.java
index 9797828..ba37b9b 100644
--- a/media/java/android/media/SubtitleData.java
+++ b/media/java/android/media/SubtitleData.java
@@ -79,6 +79,14 @@
}
}
+ /** @hide */
+ public SubtitleData(int trackIndex, long startTimeUs, long durationUs, byte[] data) {
+ mTrackIndex = trackIndex;
+ mStartTimeUs = startTimeUs;
+ mDurationUs = durationUs;
+ mData = data;
+ }
+
/**
* Returns the index of the MediaPlayer track which contains this subtitle data.
* @return an index in the array returned by {@link MediaPlayer#getTrackInfo()}.
diff --git a/media/java/android/media/TimedMetaData.java b/media/java/android/media/TimedMetaData.java
index 03c8770..97e6bfa 100644
--- a/media/java/android/media/TimedMetaData.java
+++ b/media/java/android/media/TimedMetaData.java
@@ -48,6 +48,14 @@
}
/**
+ * @hide
+ */
+ public TimedMetaData(long timestampUs, byte[] metaData) {
+ mTimestampUs = timestampUs;
+ mMetaData = metaData;
+ }
+
+ /**
* @return the timestamp associated with this metadata access unit in microseconds;
* 0 denotes playback start.
*/
diff --git a/media/java/android/media/TimedText.java b/media/java/android/media/TimedText.java
index ebcea19..d8cdf9c 100644
--- a/media/java/android/media/TimedText.java
+++ b/media/java/android/media/TimedText.java
@@ -364,6 +364,16 @@
}
/**
+ * @param text the characters in the timed text.
+ * @param bounds the rectangle area or region for rendering the timed text.
+ * {@hide}
+ */
+ public TimedText(String text, Rect bounds) {
+ mTextChars = text;
+ mTextBounds = bounds;
+ }
+
+ /**
* Get the characters in the timed text.
*
* @return the characters as a String object in the TimedText. Applications
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index de9a24b..339a7ee 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -133,8 +133,10 @@
"libmediaextractor",
"libmediametrics",
"libmediaplayer2",
+ "libmediaplayer2-protos",
"libmediautils",
"libnetd_client",
+ "libprotobuf-cpp-lite",
"libstagefright_esds",
"libstagefright_foundation",
"libstagefright_httplive",
@@ -143,7 +145,7 @@
"libstagefright_nuplayer2",
"libstagefright_player2",
"libstagefright_rtsp",
- "libstagefright_timedtext",
+ "libstagefright_timedtext2",
"libunwindstack",
"libutilscallstack",
"libziparchive",
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 4265987..801dade 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -56,6 +56,10 @@
#include "android_util_Binder.h"
#include <binder/Parcel.h>
+#include "mediaplayer2.pb.h"
+
+using android::media::MediaPlayer2Proto::PlayerMessage;
+
// Modular DRM begin
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
@@ -157,7 +161,7 @@
JNIMediaPlayer2Listener(JNIEnv* env, jobject thiz, jobject weak_thiz);
~JNIMediaPlayer2Listener();
virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
- const Parcel *obj = NULL) override;
+ const PlayerMessage *obj = NULL) override;
private:
JNIMediaPlayer2Listener();
jclass mClass; // Reference to MediaPlayer2 class
@@ -190,18 +194,21 @@
env->DeleteGlobalRef(mClass);
}
-void JNIMediaPlayer2Listener::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
+void JNIMediaPlayer2Listener::notify(int64_t srcId, int msg, int ext1, int ext2,
+ const PlayerMessage* obj)
{
JNIEnv *env = JavaVMHelper::getJNIEnv();
- if (obj && obj->dataSize() > 0) {
- jobject jParcel = createJavaParcelObject(env);
- if (jParcel != NULL) {
- Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
- nativeParcel->setData(obj->data(), obj->dataSize());
- env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
- srcId, msg, ext1, ext2, jParcel);
- env->DeleteLocalRef(jParcel);
- }
+ if (obj != NULL) {
+ int size = obj->ByteSize();
+ jbyte* temp = new jbyte[size];
+ obj->SerializeToArray(temp, size);
+
+ // return the response as a byte array.
+ jbyteArray out = env->NewByteArray(size);
+ env->SetByteArrayRegion(out, 0, size, temp);
+ env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
+ srcId, msg, ext1, ext2, out);
+ delete[] temp;
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
srcId, msg, ext1, ext2, NULL);
@@ -911,6 +918,9 @@
return false;
}
+ // TODO: parcelForJavaObject() shouldn't be used since it's dependent on
+ // framework's Parcel implementation. This setParameter() is used
+ // only with AudioAttribute. Can this be used as jobject with JAudioTrack?
Parcel *request = parcelForJavaObject(env, java_request);
status_t err = mp->setParameter(key, *request);
if (err == OK) {
@@ -978,26 +988,35 @@
process_media_player_call( env, thiz, mp->setVolume((float) leftVolume, (float) rightVolume), NULL, NULL );
}
-// Sends the request and reply parcels to the media player via the
-// binder interface.
-static jint
-android_media_MediaPlayer2_invoke(JNIEnv *env, jobject thiz,
- jobject java_request, jobject java_reply)
-{
+static jbyteArray
+android_media_MediaPlayer2_invoke(JNIEnv *env, jobject thiz, jbyteArray requestData) {
sp<MediaPlayer2> media_player = getMediaPlayer(env, thiz);
- if (media_player == NULL ) {
+ if (media_player == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return UNKNOWN_ERROR;
+ return NULL;
}
- Parcel *request = parcelForJavaObject(env, java_request);
- Parcel *reply = parcelForJavaObject(env, java_reply);
+ // Get the byte[] pointer and data length.
+ jbyte* pData = env->GetByteArrayElements(requestData, NULL);
+ jsize pDataLen = env->GetArrayLength(requestData);
- request->setDataPosition(0);
+ // Deserialize from the byte stream.
+ PlayerMessage request;
+ PlayerMessage response;
+ request.ParseFromArray(pData, pDataLen);
- // Don't use process_media_player_call which use the async loop to
- // report errors, instead returns the status.
- return (jint) media_player->invoke(*request, reply);
+ media_player->invoke(request, &response);
+
+ int size = response.ByteSize();
+ jbyte* temp = new jbyte[size];
+ response.SerializeToArray(temp, size);
+
+ // return the response as a byte array.
+ jbyteArray out = env->NewByteArray(size);
+ env->SetByteArrayRegion(out, 0, size, temp);
+ delete[] temp;
+
+ return out;
}
// Sends the new filter to the client.
@@ -1068,7 +1087,7 @@
}
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
- "(Ljava/lang/Object;JIIILjava/lang/Object;)V");
+ "(Ljava/lang/Object;JIII[B)V");
if (fields.post_event == NULL) {
return;
}
@@ -1512,7 +1531,7 @@
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping},
{"_setVolume", "(FF)V", (void *)android_media_MediaPlayer2_setVolume},
- {"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer2_invoke},
+ {"_invoke", "([B)[B", (void *)android_media_MediaPlayer2_invoke},
{"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer2_setMetadataFilter},
{"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer2_getMetadata},
{"native_init", "()V", (void *)android_media_MediaPlayer2_native_init},
diff --git a/media/proto/Android.bp b/media/proto/Android.bp
new file mode 100644
index 0000000..50d44c3
--- /dev/null
+++ b/media/proto/Android.bp
@@ -0,0 +1,20 @@
+java_library_static {
+ name: "mediaplayer2-protos",
+ host_supported: true,
+ proto: {
+ type: "lite",
+ },
+ srcs: ["mediaplayer2.proto"],
+ no_framework_libs: true,
+ jarjar_rules: "jarjar-rules.txt",
+}
+
+cc_library_static {
+ name: "libmediaplayer2-protos",
+ host_supported: true,
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["mediaplayer2.proto"],
+}
diff --git a/media/proto/jarjar-rules.txt b/media/proto/jarjar-rules.txt
new file mode 100644
index 0000000..7be6e73
--- /dev/null
+++ b/media/proto/jarjar-rules.txt
@@ -0,0 +1,2 @@
+rule com.google.protobuf.** com.android.framework.protobuf.@1
+
diff --git a/media/proto/mediaplayer2.proto b/media/proto/mediaplayer2.proto
new file mode 100644
index 0000000..6287d6cd
--- /dev/null
+++ b/media/proto/mediaplayer2.proto
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+// C++ namespace: android::media:MediaPlayer2Proto:
+package android.media.MediaPlayer2Proto;
+
+option java_package = "android.media";
+option java_outer_classname = "MediaPlayer2Proto";
+
+message Value {
+ // The kind of value.
+ oneof kind {
+ // Represents a boolean value.
+ bool bool_value = 1;
+ // Represents an int32 value.
+ int32 int32_value = 2;
+ // Represents an uint32 value.
+ uint32 uint32_value = 3;
+ // Represents an int64 value.
+ int64 int64_value = 4;
+ // Represents an uint64 value.
+ uint64 uint64_value = 5;
+ // Represents a float value.
+ double float_value = 6;
+ // Represents a double value.
+ double double_value = 7;
+ // Represents a string value.
+ string string_value = 8;
+ // Represents a bytes value.
+ bytes bytes_value = 9;
+ }
+}
+
+message PlayerMessage {
+ repeated Value values = 1;
+}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 4fb5e74..43847cc 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -64,6 +64,7 @@
"libsensor",
"libandroid_runtime",
"libnetd_client",
+ "libhwui",
],
static_libs: [
diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp
index b266881..ced279277 100644
--- a/native/android/surface_texture.cpp
+++ b/native/android/surface_texture.cpp
@@ -21,15 +21,16 @@
#include <utils/Log.h>
-#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
+#include "surfacetexture/SurfaceTexture.h"
+
using namespace android;
struct ASurfaceTexture {
- sp<GLConsumer> consumer;
+ sp<SurfaceTexture> consumer;
sp<IGraphicBufferProducer> producer;
};
diff --git a/packages/DefaultContainerService/res/values-fr-rCA/strings.xml b/packages/DefaultContainerService/res/values-fr-rCA/strings.xml
index 69c4e99..353d0b4 100644
--- a/packages/DefaultContainerService/res/values-fr-rCA/strings.xml
+++ b/packages/DefaultContainerService/res/values-fr-rCA/strings.xml
@@ -20,5 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="service_name" msgid="4841491635055379553">"Aide accès au paquet"</string>
+ <string name="service_name" msgid="4841491635055379553">"Aide Accès au paquet"</string>
</resources>
diff --git a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
index 8ea6a3a..647334b 100644
--- a/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
+++ b/packages/DefaultContainerService/res/values-pt-rPT/strings.xml
@@ -20,5 +20,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="service_name" msgid="4841491635055379553">"Ajuda p/ aceder a pacotes"</string>
+ <string name="service_name" msgid="4841491635055379553">"Ajuda acesso a pacotes"</string>
</resources>
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 0a720a5..62207c5 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -145,6 +145,7 @@
}
}
+ @GuardedBy("mRootsLock")
private void updateVolumesLocked() {
mRoots.clear();
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 2be9311..7c81399 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -106,6 +106,14 @@
</intent-filter>
</receiver>
+ <receiver android:name=".PackageInstalledReceiver"
+ android:exported="true">
+ <intent-filter android:priority="1">
+ <action android:name="android.intent.action.PACKAGE_ADDED" />
+ <data android:scheme="package" />
+ </intent-filter>
+ </receiver>
+
<activity android:name=".UninstallUninstalling"
android:excludeFromRecents="true"
android:exported="false" />
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
new file mode 100644
index 0000000..67ac99f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Receive new app installed broadcast and notify user new app installed.
+ */
+public class PackageInstalledReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "PackageInstalledReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // TODO: Add logic to handle new app installed.
+ }
+}
diff --git a/packages/SettingsLib/HelpUtils/res/values-af/strings.xml b/packages/SettingsLib/HelpUtils/res/values-af/strings.xml
new file mode 100644
index 0000000..8bf8545
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-af/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Hulp en terugvoer"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-am/strings.xml b/packages/SettingsLib/HelpUtils/res/values-am/strings.xml
new file mode 100644
index 0000000..10a276c
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-am/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"እገዛ እና ግብረመልስ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml
new file mode 100644
index 0000000..cfbe26a
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ar/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"المساعدة والتعليقات"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-as/strings.xml b/packages/SettingsLib/HelpUtils/res/values-as/strings.xml
new file mode 100644
index 0000000..0a3cb6d
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-as/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"সহায় আৰু প্ৰতিক্ৰিয়া"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-az/strings.xml b/packages/SettingsLib/HelpUtils/res/values-az/strings.xml
new file mode 100644
index 0000000..a7353d9
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-az/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Yardım və rəy"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/HelpUtils/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..af5aee7
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Pomoć i povratne informacije"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-be/strings.xml b/packages/SettingsLib/HelpUtils/res/values-be/strings.xml
new file mode 100644
index 0000000..87f8e74
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-be/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Даведка і водгукі"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-bg/strings.xml b/packages/SettingsLib/HelpUtils/res/values-bg/strings.xml
new file mode 100644
index 0000000..d51626b
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-bg/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Помощ и отзиви"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-bn/strings.xml b/packages/SettingsLib/HelpUtils/res/values-bn/strings.xml
new file mode 100644
index 0000000..57b7218
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"সহায়তা ও মতামত"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-bs/strings.xml b/packages/SettingsLib/HelpUtils/res/values-bs/strings.xml
new file mode 100644
index 0000000..af5aee7
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-bs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Pomoć i povratne informacije"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ca/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ca/strings.xml
new file mode 100644
index 0000000..b62c6cd
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ca/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ajuda i suggeriments"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-cs/strings.xml b/packages/SettingsLib/HelpUtils/res/values-cs/strings.xml
new file mode 100644
index 0000000..2da1b9c
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-cs/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Nápověda a zpětná vazba"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-da/strings.xml b/packages/SettingsLib/HelpUtils/res/values-da/strings.xml
new file mode 100644
index 0000000..4f364743
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-da/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Hjælp og feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-de/strings.xml b/packages/SettingsLib/HelpUtils/res/values-de/strings.xml
new file mode 100644
index 0000000..e82e92b
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-de/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Hilfe & Feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-el/strings.xml b/packages/SettingsLib/HelpUtils/res/values-el/strings.xml
new file mode 100644
index 0000000..bd41d3e
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-el/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Βοήθεια και σχόλια"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rAU/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..dce3496
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rAU/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Help & feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rCA/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..dce3496
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Help & feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rGB/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..dce3496
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rGB/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Help & feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rIN/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..dce3496
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rIN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Help & feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-en-rXC/strings.xml b/packages/SettingsLib/HelpUtils/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..21084fb
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-en-rXC/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Help & feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-es-rUS/strings.xml b/packages/SettingsLib/HelpUtils/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..3bc97c6
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-es-rUS/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ayuda y comentarios"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-es/strings.xml b/packages/SettingsLib/HelpUtils/res/values-es/strings.xml
new file mode 100644
index 0000000..8567018
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-es/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ayuda y sugerencias"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-et/strings.xml b/packages/SettingsLib/HelpUtils/res/values-et/strings.xml
new file mode 100644
index 0000000..177c3e0
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-et/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Abi ja tagasiside"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-eu/strings.xml b/packages/SettingsLib/HelpUtils/res/values-eu/strings.xml
new file mode 100644
index 0000000..ff22ba5
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-eu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Laguntza eta iritziak"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-fa/strings.xml b/packages/SettingsLib/HelpUtils/res/values-fa/strings.xml
new file mode 100644
index 0000000..d07d8f6
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-fa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"راهنما و بازخورد"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-fi/strings.xml b/packages/SettingsLib/HelpUtils/res/values-fi/strings.xml
new file mode 100644
index 0000000..e94ebc5
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-fi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ohje ja palaute"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-fr-rCA/strings.xml b/packages/SettingsLib/HelpUtils/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..fd59425
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-fr-rCA/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Aide et rétroaction"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-fr/strings.xml b/packages/SettingsLib/HelpUtils/res/values-fr/strings.xml
new file mode 100644
index 0000000..23f0b3b
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-fr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Aide et commentaires"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-gl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-gl/strings.xml
new file mode 100644
index 0000000..c8cc6b6
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-gl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Axuda e comentarios"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-gu/strings.xml b/packages/SettingsLib/HelpUtils/res/values-gu/strings.xml
new file mode 100644
index 0000000..a53c6d2
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-gu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"સહાય અને પ્રતિસાદ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-hr/strings.xml b/packages/SettingsLib/HelpUtils/res/values-hr/strings.xml
new file mode 100644
index 0000000..af5aee7
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-hr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Pomoć i povratne informacije"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-hu/strings.xml b/packages/SettingsLib/HelpUtils/res/values-hu/strings.xml
new file mode 100644
index 0000000..bcf2734
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-hu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Súgó és visszajelzés"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-hy/strings.xml b/packages/SettingsLib/HelpUtils/res/values-hy/strings.xml
new file mode 100644
index 0000000..ea14fa5
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-hy/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Օգնություն և հետադարձ կապ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-in/strings.xml b/packages/SettingsLib/HelpUtils/res/values-in/strings.xml
new file mode 100644
index 0000000..3055c7f
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-in/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Bantuan & masukan"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-is/strings.xml b/packages/SettingsLib/HelpUtils/res/values-is/strings.xml
new file mode 100644
index 0000000..1feaf4a3
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-is/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Hjálp og ábendingar"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-it/strings.xml b/packages/SettingsLib/HelpUtils/res/values-it/strings.xml
new file mode 100644
index 0000000..8e4d78d
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-it/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Guida e feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-iw/strings.xml b/packages/SettingsLib/HelpUtils/res/values-iw/strings.xml
new file mode 100644
index 0000000..ab52996
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-iw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"עזרה ומשוב"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ja/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ja/strings.xml
new file mode 100644
index 0000000..8c75d32
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ja/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"ヘルプとフィードバック"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ka/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ka/strings.xml
new file mode 100644
index 0000000..53b956f
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ka/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"დახმარება და გამოხმაურება"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-kk/strings.xml b/packages/SettingsLib/HelpUtils/res/values-kk/strings.xml
new file mode 100644
index 0000000..ef351ac
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-kk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Анықтама және пікір"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-km/strings.xml b/packages/SettingsLib/HelpUtils/res/values-km/strings.xml
new file mode 100644
index 0000000..b2b0615
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-km/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"ជំនួយ និងមតិកែលម្អ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-kn/strings.xml b/packages/SettingsLib/HelpUtils/res/values-kn/strings.xml
new file mode 100644
index 0000000..8a86559
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-kn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ko/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ko/strings.xml
new file mode 100644
index 0000000..f974a51
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ko/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"도움말 및 의견"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ky/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ky/strings.xml
new file mode 100644
index 0000000..3c0cf87
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ky/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Жардам/Пикир билдирүү"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-lo/strings.xml b/packages/SettingsLib/HelpUtils/res/values-lo/strings.xml
new file mode 100644
index 0000000..3765d6e
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-lo/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"ຊ່ວຍເຫຼືອ ແລະ ຕິຊົມ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-lt/strings.xml b/packages/SettingsLib/HelpUtils/res/values-lt/strings.xml
new file mode 100644
index 0000000..d7bce29
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-lt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Pagalba ir atsiliepimai"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-lv/strings.xml b/packages/SettingsLib/HelpUtils/res/values-lv/strings.xml
new file mode 100644
index 0000000..77496a9
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-lv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Palīdzība un atsauksmes"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-mk/strings.xml b/packages/SettingsLib/HelpUtils/res/values-mk/strings.xml
new file mode 100644
index 0000000..516c186
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-mk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Помош и повратни информации"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ml/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ml/strings.xml
new file mode 100644
index 0000000..d2a7452
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ml/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"സഹായം & ഫീഡ്ബാക്ക്"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-mn/strings.xml b/packages/SettingsLib/HelpUtils/res/values-mn/strings.xml
new file mode 100644
index 0000000..c3ebe8b
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-mn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Тусламж & санал хүсэлт"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-mr/strings.xml b/packages/SettingsLib/HelpUtils/res/values-mr/strings.xml
new file mode 100644
index 0000000..7f3a881
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-mr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"मदत आणि फीडबॅक"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ms/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ms/strings.xml
new file mode 100644
index 0000000..7c7c971
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ms/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Bantuan & maklum balas"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-my/strings.xml b/packages/SettingsLib/HelpUtils/res/values-my/strings.xml
new file mode 100644
index 0000000..f051860
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-my/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"အကူအညီနှင့် အကြံပြုချက်"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-nb/strings.xml b/packages/SettingsLib/HelpUtils/res/values-nb/strings.xml
new file mode 100644
index 0000000..96886f6
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-nb/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Hjelp og tilbakemelding"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ne/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ne/strings.xml
new file mode 100644
index 0000000..40508ad
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ne/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"मद्दत र प्रतिक्रिया"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
new file mode 100644
index 0000000..a034d29
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Help en feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-or/strings.xml b/packages/SettingsLib/HelpUtils/res/values-or/strings.xml
new file mode 100644
index 0000000..e91cb61
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-or/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"ସହାୟତା ଓ ମତାମତ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-pa/strings.xml b/packages/SettingsLib/HelpUtils/res/values-pa/strings.xml
new file mode 100644
index 0000000..d847a59
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-pa/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"ਮਦਦ ਅਤੇ ਵਿਚਾਰ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-pl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-pl/strings.xml
new file mode 100644
index 0000000..e137a6b
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-pl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Pomoc i opinie"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-pt-rBR/strings.xml b/packages/SettingsLib/HelpUtils/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..73dfc49
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-pt-rBR/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ajuda e feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-pt-rPT/strings.xml b/packages/SettingsLib/HelpUtils/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..baac2e8
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-pt-rPT/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ajuda e comentários"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-pt/strings.xml b/packages/SettingsLib/HelpUtils/res/values-pt/strings.xml
new file mode 100644
index 0000000..73dfc49
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-pt/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ajuda e feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ro/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ro/strings.xml
new file mode 100644
index 0000000..1422912
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ro/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ajutor și feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ru/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ru/strings.xml
new file mode 100644
index 0000000..d7d1108
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ru/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Справка и отзывы"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-si/strings.xml b/packages/SettingsLib/HelpUtils/res/values-si/strings.xml
new file mode 100644
index 0000000..719d7dc
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-si/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"උදවු සහ ප්රතිපෝෂණ"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-sk/strings.xml b/packages/SettingsLib/HelpUtils/res/values-sk/strings.xml
new file mode 100644
index 0000000..a9eede3
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-sk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Pomocník a spätná väzba"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-sl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-sl/strings.xml
new file mode 100644
index 0000000..81db744
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-sl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Pomoč in povratne informacije"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-sq/strings.xml b/packages/SettingsLib/HelpUtils/res/values-sq/strings.xml
new file mode 100644
index 0000000..fddeff6
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-sq/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Ndihma dhe komentet"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-sr/strings.xml b/packages/SettingsLib/HelpUtils/res/values-sr/strings.xml
new file mode 100644
index 0000000..5d258060
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-sr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Помоћ и повратне информације"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-sv/strings.xml b/packages/SettingsLib/HelpUtils/res/values-sv/strings.xml
new file mode 100644
index 0000000..df1c891
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-sv/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Hjälp och feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-sw/strings.xml b/packages/SettingsLib/HelpUtils/res/values-sw/strings.xml
new file mode 100644
index 0000000..a4bb726
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-sw/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Usaidizi na maoni"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ta/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ta/strings.xml
new file mode 100644
index 0000000..91e8e46
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ta/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"உதவி & கருத்து"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-te/strings.xml b/packages/SettingsLib/HelpUtils/res/values-te/strings.xml
new file mode 100644
index 0000000..753b320
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"సహాయం & అభిప్రాయం"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-th/strings.xml b/packages/SettingsLib/HelpUtils/res/values-th/strings.xml
new file mode 100644
index 0000000..53d16a6
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-th/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"ความช่วยเหลือและความคิดเห็น"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-tl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-tl/strings.xml
new file mode 100644
index 0000000..7edcd18
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-tl/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Tulong at feedback"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-tr/strings.xml b/packages/SettingsLib/HelpUtils/res/values-tr/strings.xml
new file mode 100644
index 0000000..263226f
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-tr/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Yardım ve geri bildirim"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-uk/strings.xml b/packages/SettingsLib/HelpUtils/res/values-uk/strings.xml
new file mode 100644
index 0000000..ebef521
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-uk/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Довідка й відгуки"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-ur/strings.xml b/packages/SettingsLib/HelpUtils/res/values-ur/strings.xml
new file mode 100644
index 0000000..084bf29
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-ur/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"مدد اور تاثرات"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-uz/strings.xml b/packages/SettingsLib/HelpUtils/res/values-uz/strings.xml
new file mode 100644
index 0000000..2106bed
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-uz/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Yordam va fikr-mulohaza"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-vi/strings.xml b/packages/SettingsLib/HelpUtils/res/values-vi/strings.xml
new file mode 100644
index 0000000..68ce6d1
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-vi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Trợ giúp & phản hồi"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-zh-rCN/strings.xml b/packages/SettingsLib/HelpUtils/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..a6d9f49
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-zh-rCN/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"帮助和反馈"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-zh-rHK/strings.xml b/packages/SettingsLib/HelpUtils/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..a6eae04
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-zh-rHK/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"說明與意見反映"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-zh-rTW/strings.xml b/packages/SettingsLib/HelpUtils/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..d66802c
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-zh-rTW/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"說明與意見回饋"</string>
+</resources>
diff --git a/packages/SettingsLib/HelpUtils/res/values-zu/strings.xml b/packages/SettingsLib/HelpUtils/res/values-zu/strings.xml
new file mode 100644
index 0000000..32993ce8
--- /dev/null
+++ b/packages/SettingsLib/HelpUtils/res/values-zu/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="help_feedback_label" msgid="4550436169116444686">"Usizo nempendulo"</string>
+</resources>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 296a135..aa96444 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Groter"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Grootste"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Gepasmaak (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Hulp en terugvoer"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Kieslys"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Voer wagwoord in om fabriekterugstelling in demonstrasiemodus uit te voer"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Volgende"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index ab52bcb..452206c 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ተለቅ ያለ"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"በጣም ተለቅ ያለ"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ብጁ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"እገዛ እና ግብረመልስ"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"ምናሌ"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"የፋብሪካ ዳግም ማስጀመር በማሳያ ሁነታ ውስጥ ለማከናወን የይለፍ ቃል ያስገቡ"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"ቀጣይ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e8811eb..e67e1ec 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"أكبر"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"أكبر مستوى"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"مخصص (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"المساعدة والتعليقات"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"القائمة"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"إدخال كلمة المرور لإعادة الضبط بحسب بيانات المصنع في الوضع التجريبي"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"التالي"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 0adac28..7311661 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"বৃহত্তৰ"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"সকলোতকৈ ডাঙৰ"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"নিজৰ উপযোগিতা অনুযায়ী (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"সহায় আৰু ফীডবেক"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"মেনু"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ডেম’ ম\'ডত ফেক্টৰী ৰিছেট কৰিবলৈ পাছৱৰ্ড দিয়ক"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"পৰৱৰ্তী"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 8f974fe..750bf5d 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Daha böyük"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ən böyük"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Fərdi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Yardım və rəy"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Demo rejimində sıfırlamaq üçün parol daxil edin"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Növbəti"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 1e3eba6..cd8096c 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Veći"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveći"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagođeni (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Unesite lozinku da biste obavili resetovanje na fabrička podešavanja u režimu demonstracije"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Dalje"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 42fda63..072913a 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Большы"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Самы вялікі"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Карыстальніцкі (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Даведка і водгукі"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Каб выканаць скід да заводскіх налад у дэманстрацыйным рэжыме, увядзіце пароль"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Далей"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index a309449..a5ce523 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"По-голямо"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Най-голямо"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Персонализирано (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Помощ и отзиви"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Въведете парола за възст. на фабр. настройки в демонстр. режим"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Напред"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index ad4ef2f..c053771 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"খুব বড়"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"বৃহত্তম"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"কাস্টম (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"সহায়তা ও মতামত"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"মেনু"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ডেমো মোডে ফ্যাক্টরি রিসেট করতে পাসওয়ার্ড দিন"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"পরবর্তী"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 4332682..764bc19 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Vrlo veliko"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveći"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagodi (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Unesite lozinku da izvršite vraćanje na fabričke postavke u načinu demonstracije"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Naprijed"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 7750ef2..8309c41a 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Més gran"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Més grans possible"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalitzat (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda i suggeriments"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Introdueix la contrasenya per restablir les dades de fàbrica en mode de demostració"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Següent"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 72b3389..bd8bc2b 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Větší"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Největší"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Vlastní (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Nápověda a zpětná vazba"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Nabídka"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Chcete-li v ukázkovém režimu obnovit zařízení do továrního nastavení, zadejte heslo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Další"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index ca96070..ab81346 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -196,7 +196,7 @@
<string name="bugreport_in_power_summary" msgid="1778455732762984579">"Vis en knap til oprettelse af fejlrapporter i menu for slukknap"</string>
<string name="keep_screen_on" msgid="1146389631208760344">"Lås ikke"</string>
<string name="keep_screen_on_summary" msgid="2173114350754293009">"Skærmen går ikke i dvale under opladning"</string>
- <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Aktivér Bluetooth HCI snoop log"</string>
+ <string name="bt_hci_snoop_log" msgid="3340699311158865670">"Aktivér Bluetooth HCI spionlog"</string>
<string name="bt_hci_snoop_log_summary" msgid="366083475849911315">"Gem alle Bluetooth HCI-pakker i en fil (slå Bluetooth fra og til igen, når du har ændret denne indstilling)"</string>
<string name="oem_unlock_enable" msgid="6040763321967327691">"OEM-oplåsning"</string>
<string name="oem_unlock_enable_summary" msgid="4720281828891618376">"Tillad, at startindlæseren låses op"</string>
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Større"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Størst"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tilpasset (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Hjælp og feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Angiv adgangskode for at gendanne fabriksdata i demotilstand"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Næste"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 9db490b..2432a10 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Größer"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Am größten"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Benutzerdefiniert (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Hilfe & Feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Passwort eingeben, um Gerät im Demomodus auf Werkseinstellungen zurückzusetzen"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Weiter"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 2ba543e..6f2889c 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Πιο μεγάλα"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Μεγαλύτερα"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Προσαρμοσμένη (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Βοήθεια και σχόλια"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Μενού"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Εισαγάγετε κωδικό πρόσβασης για επαναφορά εργοστασιακών ρυθμίσεων στη λειτουργία επίδειξης"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Επόμενο"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 747fdc7..154aa7d9 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Larger"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Help & feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Next"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 747fdc7..154aa7d9 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Larger"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Help & feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Next"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 747fdc7..154aa7d9 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Larger"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Help & feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Next"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 747fdc7..154aa7d9 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Larger"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Help & feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Next"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 33b9f1d..e9746c0 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Larger"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Largest"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Help & feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Next"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index f665b77..706e1d3 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Más grande"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Máximo"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ayuda y comentarios"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Ingresa contraseña y restablece en demo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Siguiente"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 9cdde92..f9eddd9 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -242,7 +242,7 @@
<string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Elige el tamaño del Logger por búfer"</string>
<string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"¿Borrar almacenamiento continuo del registrador?"</string>
<string name="dev_logpersist_clear_warning_message" msgid="2256582531342994562">"Cuando ya no supervisamos la actividad con el registrador de forma continua, estamos obligados a borrar los datos del registrador almacenados en el dispositivo."</string>
- <string name="select_logpersist_title" msgid="7530031344550073166">"Guardar datos de forma continua"</string>
+ <string name="select_logpersist_title" msgid="7530031344550073166">"Guardar datos de registro de forma continua"</string>
<string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Seleccionar búferes de registro para guardarlos de forma continua en dispositivo"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Seleccionar configuración de USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Seleccionar configuración de USB"</string>
@@ -396,7 +396,7 @@
<string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada por el administrador"</string>
<string name="enabled_by_admin" msgid="5302986023578399263">"Habilitada por el administrador"</string>
- <string name="disabled_by_admin" msgid="8505398946020816620">"Inhabilitada por el administrador"</string>
+ <string name="disabled_by_admin" msgid="8505398946020816620">"Inhabilitado por el administrador"</string>
<string name="disabled" msgid="9206776641295849915">"Inhabilitada"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Autorizadas"</string>
<string name="external_source_untrusted" msgid="2677442511837596726">"No autorizadas"</string>
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Más grande"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Lo más grande posible"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ayuda y sugerencias"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Escribe una contraseña para restablecer estado de fábrica en modo demostración"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Siguiente"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 2b44321..ac31880 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Suurem"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Suurim"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Kohandatud (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Abi ja tagasiside"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menüü"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Sisestage parool, et demorežiimis tehaseseadetele lähtestada"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Järgmine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 4ad60b3..2eee948 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Oso handia"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Handiena"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Pertsonalizatua (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Laguntza eta iritziak"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menua"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Idatzi pasahitza jatorrizko ezarpenak demo moduan berrezartzeko"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Hurrengoa"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1d08821..6efd4fe 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"بزرگتر"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"بزرگترین"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"سفارشی (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"راهنما و بازخورد"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"منو"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"برای انجام بازنشانی کارخانهای در حالت نمایشی، گذرواژه را وارد کنید"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"بعدی"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 7c253fc..4d64e81 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Suurempi"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Suurin"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Muokattu (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ohje ja palaute"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Valikko"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Palauta tehdasasetukset antamalla salasana"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Seuraava"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 99906ca..8e813ea 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Plus grande"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"La plus grande"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personnalisée (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Aide et commentaires"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Entrez m. passe pour réinit. en mode démo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Suivant"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 7a51faa..baf8ed0 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -246,7 +246,7 @@
<string name="select_logpersist_dialog_title" msgid="4003400579973269060">"Sélectionner les mémoires tampon journal à stocker en permanence sur l\'appareil"</string>
<string name="select_usb_configuration_title" msgid="2649938511506971843">"Sélectionner une configuration USB"</string>
<string name="select_usb_configuration_dialog_title" msgid="6385564442851599963">"Sélectionner une configuration USB"</string>
- <string name="allow_mock_location" msgid="2787962564578664888">"Positions fictives"</string>
+ <string name="allow_mock_location" msgid="2787962564578664888">"Autoriser les positions fictives"</string>
<string name="allow_mock_location_summary" msgid="317615105156345626">"Autoriser les positions fictives"</string>
<string name="debug_view_attributes" msgid="6485448367803310384">"Activer inspect. attribut affich."</string>
<string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Maintenir les données mobiles à l\'état actif, même lorsque le Wi‑Fi est actif (pour changer rapidement de réseau)"</string>
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Très grand"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Extrêmement grand"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personnalisé (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Aide et commentaires"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Saisir mot de passe pour rétablir conf. d\'usine en mode démo."</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Suivant"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 5bc6163..dc22b25 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Máis grande"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"O máis grande"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Axuda e comentarios"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menú"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Insire contrasinal para restablec. en demostración"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Seguinte"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index b523ae2..7943d51 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"વધુ મોટું"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"સૌથી મોટું"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"કસ્ટમ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"સહાય અને પ્રતિસાદ"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"મેનુ"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ડેમો મોડમાં ફેક્ટરી રીસેટ પાસવર્ડ દાખલ કરો"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"આગલું"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index cd12f21..3ec7e58 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"अधिक बड़ा"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सबसे बड़ा"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"कस्टम (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"सहायता और फ़ीडबैक"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"मेन्यू"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"डेमो मोड में फ़ैक्टरी रीसेट के लिए पासवर्ड डालें"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"आगे"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 9c83640..ce8e6ec 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Veće"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najveće"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Prilagođeno (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Pomoć i povratne informacije"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Izbornik"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Unesite zaporku za resetiranje u demo načinu"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Dalje"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 25d11aa..1c79199 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Nagyobb"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Legnagyobb"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Egyéni (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Súgó és visszajelzés"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Írja be a jelszót a visszaállításhoz"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Következő"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index f811312c..84ee039 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Ավելի մեծ"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ամենամեծ"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Հատուկ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Օգնություն և հետադարձ կապ"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Ընտրացանկ"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Մուտքագրեք գաղտնաբառը՝ ցուցադրական ռեժիմում գործարանային վերակայում կատարելու համար"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Հաջորդը"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 39a88e5..9356c85 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -281,7 +281,7 @@
<string name="strict_mode_summary" msgid="142834318897332338">"Kedipkan layar saat apl beroperasi lama pada utas utama"</string>
<string name="pointer_location" msgid="6084434787496938001">"Lokasi penunjuk"</string>
<string name="pointer_location_summary" msgid="840819275172753713">"Hamparan layar menampilkan data sentuhan saat ini"</string>
- <string name="show_touches" msgid="2642976305235070316">"Tampilkan ketukan"</string>
+ <string name="show_touches" msgid="2642976305235070316">"Tampilkan tap"</string>
<string name="show_touches_summary" msgid="6101183132903926324">"Tampilkan masukan visual untuk ketukan"</string>
<string name="show_screen_updates" msgid="5470814345876056420">"Lihat pembaruan permukaan"</string>
<string name="show_screen_updates_summary" msgid="2569622766672785529">"Sorot seluruh permukaan jendela saat diperbarui"</string>
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Lebih besar"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Terbesar"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"(<xliff:g id="DENSITYDPI">%d</xliff:g>) khusus"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Bantuan & masukan"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Masukkan sandi untuk mengembalikan ke setelan pabrik dalam mode demo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Berikutnya"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index afb10a9..ca5e1f5 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Stærra"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Stærst"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Sérsniðið (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Hjálp og ábendingar"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Valmynd"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Sláðu inn aðgangsorð til að framkvæma núllstillingu í sýnisútgáfu"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Áfram"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 24eef7a..88a5bb8 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Più grande"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Massimo"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizzato (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Guida e feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Inserisci la password per eseguire il ripristino dei dati di fabbrica in modalità demo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Avanti"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index e9c44c9..cec35cf 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"יותר גדול"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"הכי גדול"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"מותאם אישית (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"עזרה ומשוב"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"תפריט"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"הזן סיסמה כדי לבצע איפוס להגדרות היצרן במצב הדגמה"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"הבא"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 0cc2d17..9cfdadc 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"特大"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"カスタム(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"ヘルプとフィードバック"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"メニュー"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"デモモードで初期状態にリセットするには、パスワードを入力してください"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"次へ"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index daca5b9..1083025 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"უფრო დიდი"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"უდიდესი"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"მორგებული (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"დახმარება და გამოხმაურება"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"მენიუ"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"შეიყვანეთ პაროლი დემო-რეჟიმში ქარხნულ მდგომარეობაზე დასაბრუნებლად"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"შემდეგი"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index b95d6e9e..a122d91 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Үлкенірек"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Ең үлкен"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Арнаулы (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Анықтама және пікір"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Mәзір"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Демо режимде зауыттық мәндерге қайтару үшін құпия сөзді енгізу"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Келесі"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 5bf38c0..7ffd766 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ធំជាង"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ធំបំផុត"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ផ្ទាល់ខ្លួន (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"ជំនួយ និងមតិស្ថាបនា"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"ម៉ឺនុយ"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"បញ្ចូលពាក្យសម្ងាត់ ដើម្បីកំណត់ឧបករណ៍ឡើងវិញដូចពេលចេញពីរោងចក្រ នៅក្នុងមុខងារសាកល្បង"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"បន្ទាប់"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 923547f..e12cbd2 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ಸ್ವಲ್ಪ ದೊಡ್ಡ"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ದೊಡ್ಡ"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ಕಸ್ಟಮ್ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"ಮೆನು"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ಫ್ಯಾಕ್ಟರಿ ರಿಸೆಟ್ಗೆ ಪಾಸ್ವರ್ಡ್ ನಮೂದಿಸಿ"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"ಮುಂದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 9bac29d..ab2892f 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"더 크게"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"가장 크게"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"맞춤(<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"고객센터"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"메뉴"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"데모 모드에서 초기화하려면 비밀번호 입력"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"다음"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 523fabe..56b5da1 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Чоңураак"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Эң чоң"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Ыңгайлаштырылган (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Жардам жана пикир билдирүү"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Демо режиминде демейки жөндөөлөргө кайтаруу үчүн сырсөздү киргизиңиз"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Кийинки"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index f85a691..eb27f02 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ໃຫຍ່ກວ່າ"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ໃຫຍ່ທີ່ສຸດ"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ປັບແຕ່ງເອງ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"ຊ່ວຍເຫຼືອ & ຄຳຕິຊົມ"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"ເມນູ"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Enter password to perform factory reset in demo mode"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"ຕໍ່ໄປ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index fc2924f..44b532b 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Didesnis"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Didžiausias"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tinkintas (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Pagalba ir atsiliepimai"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Meniu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Įv. slapt. ir atk. gam. nust. dem. rež."</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Kitas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 6ce5e8e..ba87d07 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Lielāks"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Vislielākais"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Pielāgots (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Palīdzība un atsauksmes"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Izvēlne"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Iev. paroli, lai atiest. rūpnīcas iest. dem. režīmā"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Tālāk"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index c6c508e..0c5c94a 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Поголем"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Најголем"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Приспособен (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Помош и повратни информации"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Мени"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Внесете лозинка за фаб. ресет. во демо"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Следно"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 6b6462c..1a208e62 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"കൂടുതൽ വലുത്"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ഏറ്റവും വലുത്"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ഇഷ്ടാനുസൃതം ( <xliff:g id="DENSITYDPI">%d</xliff:g> )"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"സഹായവും പ്രതികരണവും"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"മെനു"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ഡെമോ മോഡിൽ ഫാക്ടറി റീസെറ്റിന് പാസ്വേഡ് നൽകുക"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"അടുത്തത്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 56f1cc0..809e82e 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Илүү том"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Хамгийн том"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Тогтмол утга (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Тусламж, санал хүсэлт"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Цэс"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Үйлдвэрийн тохиргоог демо горимд ажиллуулахын тулд нууц үг оруулна уу"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Дараагийн"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 7485886..dfa6d2f 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"आणखी मोठा"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सर्वात मोठा"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"कस्टम करा (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"मदत आणि अभिप्राय"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"मेनू"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"डेमो मोडमध्ये फॅक्टरी रीसेट करण्यासाठी पासवर्ड एंटर करा"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"पुढील"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index fdb8702..b4ed016 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Lebih besar"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Terbesar"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tersuai (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Bantuan & maklum balas"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Mskkn kta laluan utk ttpn sml kilang dlm mod demo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Seterusnya"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index dc5d924..c1ffd28 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ပိုကြီး"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"အကြီးဆုံး"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"စိတ်ကြိုက် (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"အကူအညီနှင့် အကြံပြုချက်"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"မီနူး"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ဒီမိုမုဒ်၌မူလဆက်တင်ထားရန် စကားဝှက်ထည့်ပါ"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"ရှေ့သို့"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 460eb57..5a8e01e 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Større"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Størst"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Egendefinert (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Hjelp og tilbakemelding"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Meny"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Skriv inn passordet for å tilbakestille til fabrikkstandard i demomodus"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Neste"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 763b60c..72a9abb 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"अझ ठूलो"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"सबैभन्दा ठूलो"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"आफू अनुकूल (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"मद्दत र प्रतिक्रिया"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"मेनु"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"डेमो मोडमा फ्याक्ट्री रिसेट गर्न पासवर्ड प्रविष्ट गर्नुहोस्"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"अर्को"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 264427e..f341717 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Groter"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Grootst"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Aangepast (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Help en feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Geef wachtwoord op om terug te zetten op fabrieksinstellingen in demomodus"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Volgende"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index ecdc49e..a7761e9 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ବୃହତ୍ତମ"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ବୃହତ୍ତମ"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"କଷ୍ଟମ୍ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"ସହାୟତା ଓ ମତାମତ"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"ମେନୁ"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ଡେମୋ ମୋଡ୍ରେ ଫ୍ୟାକ୍ଟୋରୀ ରିସେଟ୍ କରିବାକୁ ପାସ୍ୱାର୍ଡ ଲେଖନ୍ତୁ"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"ପରବର୍ତ୍ତୀ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 63c88cd..c7ab379 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ਥੋੜ੍ਹਾ ਵੱਡੀ"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ਸਭ ਤੋਂ ਵੱਡੀ"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"ਵਿਉਂਂਤੀ (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"ਮਦਦ & ਫੀਡਬੈਕ"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"ਮੀਨੂ"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ਡੈਮੋ ਮੋਡ \'ਚ ਫੈਕਟਰੀ ਰੀਸੈੱਟ ਲਈ ਪਾਸਵਰਡ ਦਿਓ"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"ਅੱਗੇ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 7c3da21..effa59a 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Większy"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Największy"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Niestandardowe (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Pomoc i opinie"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Wpisz hasło, by przywrócić ustawienia fabryczne w trybie demonstracyjnym"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Dalej"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 192e532..afefeda 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -77,7 +77,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Usar para compartilhamento de contatos"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Compartilhamento de conexão à Internet"</string>
<string name="bluetooth_profile_map" msgid="1019763341565580450">"Mensagens de texto"</string>
- <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso SIM"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao chip"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Áudio HD"</string>
<string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparelho auditivo"</string>
@@ -93,7 +93,7 @@
<string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Compartilhamento de conexão local de Internet c/ disposit."</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"Usar para acesso à Internet"</string>
<string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Usar para mapa"</string>
- <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use para acesso SIM"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use para acesso ao chip"</string>
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Usar para áudio de mídia"</string>
<string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usar para áudio do smartphone"</string>
<string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usado para transferência de arquivo"</string>
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Muito grande"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Maior"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizada (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Digite a senha para redef. p/ configuração original em modo demo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Próxima"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 01bc0fc..4ee4521d 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Maior"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"O maior"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizado (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e comentários"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Introduzir palavra-passe para efetuar a reposição de fábrica no modo demo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Próximo"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 192e532..afefeda 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -77,7 +77,7 @@
<string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"Usar para compartilhamento de contatos"</string>
<string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"Compartilhamento de conexão à Internet"</string>
<string name="bluetooth_profile_map" msgid="1019763341565580450">"Mensagens de texto"</string>
- <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso SIM"</string>
+ <string name="bluetooth_profile_sap" msgid="5764222021851283125">"Acesso ao chip"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"Áudio HD"</string>
<string name="bluetooth_profile_hearing_aid" msgid="7999237886427812595">"Aparelho auditivo"</string>
@@ -93,7 +93,7 @@
<string name="bluetooth_pan_nap_profile_summary_connected" msgid="1322694224800769308">"Compartilhamento de conexão local de Internet c/ disposit."</string>
<string name="bluetooth_pan_profile_summary_use_for" msgid="5736111170225304239">"Usar para acesso à Internet"</string>
<string name="bluetooth_map_profile_summary_use_for" msgid="5154200119919927434">"Usar para mapa"</string>
- <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use para acesso SIM"</string>
+ <string name="bluetooth_sap_profile_summary_use_for" msgid="7085362712786907993">"Use para acesso ao chip"</string>
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="4630849022250168427">"Usar para áudio de mídia"</string>
<string name="bluetooth_headset_profile_summary_use_for" msgid="8705753622443862627">"Usar para áudio do smartphone"</string>
<string name="bluetooth_opp_profile_summary_use_for" msgid="1255674547144769756">"Usado para transferência de arquivo"</string>
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Muito grande"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Maior"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizada (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ajuda e feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Digite a senha para redef. p/ configuração original em modo demo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Próxima"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index cc74bd3..f42363e 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Mai mare"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Cel mai mare"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Personalizat (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ajutor și feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Meniu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Introduceți parola pentru a reveni la setările din fabrică în modul demo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Înainte"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 5e1869f..451c710 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Очень крупный"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Максимальный"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Другой (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Справка/отзыв"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Чтобы сбросить настройки в деморежиме, введите пароль."</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Далее"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 1be890b..d9e69ce 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"වඩා විශාල"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"විශාලතම"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"අභිරුචි (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"උදව් සහ ප්රතිපෝෂණ"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"මෙනුව"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ආදර්ශන ප්රකාර කර්මාන්තශාලා යළි සැකසීමට මුරපදය ඇ. ක."</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"ඊළඟ"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index d1ec450..a0c1062 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Väčšie"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Najväčšie"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Vlastné (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Pomocník a spätná väzba"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Ponuka"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Zadajte heslo na obnovenie továrenských nastavení v režime ukážky"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Ďalej"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 4632550..3088d0f 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Večje"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Največje"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Po meri (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Pomoč in povratne informacije"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Meni"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Geslo za tovar. nast. v predstav. načinu"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Naprej"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 24cdcb1..8e31013 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Më i madh"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Më i madhi"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"I personalizuar (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Ndihma dhe komentet"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menyja"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Fut fjalëkalimin për të kryer rivendosje në gjendje fabrike në modalitetin e demonstrimit"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Përpara"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index ebefc65..fe97119 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Већи"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Највећи"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Прилагођени (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Помоћ и повратне информације"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Мени"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Унесите лозинку да бисте обавили ресетовање на фабричка подешавања у режиму демонстрације"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Даље"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 040aea6..11f446c 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Större"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Störst"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Anpassad (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Hjälp och feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Meny"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Ange lösenord och utför fabriksåterställning i demoläge"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Nästa"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 8be74eacf..ba3aaba 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Kubwa kiasi"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Kubwa zaidi"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Kiwango maalum (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Usaidizi na maoni"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Weka nenosiri ili urejeshe mipangilio ya kiwandani ikiwa katika hali ya onyesho."</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Inayofuata"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 225005d..63d5e75 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"கொஞ்சம் பெரியது"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"மிகப் பெரியது"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"தனிப்பயன் (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"உதவி & கருத்து"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"மெனு"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"டெமோ பயன்முறையில் ஆரம்பநிலை மீட்டமைவைச் செயல்படுத்த, கடவுச்சொல்லை உள்ளிடவும்"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"அடுத்து"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 531001c..4af461d0 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"చాలా పెద్దగా"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"అతి పెద్దగా"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"అనుకూలం (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"సహాయం & అభిప్రాయం"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"మెను"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"డెమో మోడ్లో ఫ్యాక్టరీ రీసెట్ను నిర్వహించడానికి పాస్వర్డ్ను నమోదు చేయండి"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"తర్వాత"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index cf214e1..9cc5c73 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"ใหญ่ขึ้น"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"ใหญ่ที่สุด"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"กำหนดเอง (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"ความช่วยเหลือและความคิดเห็น"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"เมนู"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ป้อนรหัสผ่านเพื่อรีเซ็ตค่าในโหมดสาธิต"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"ถัดไป"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index aee95ea..49abe42 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Mas malaki"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Pinakamalaki"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Custom (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Tulong at feedback"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Ilagay ang password upang mag-factory reset sa demo mode"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Susunod"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index e4b23f96..23a96d9 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Daha büyük"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"En büyük"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Özel (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Yardım ve geri bildirim"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menü"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Demo modunda sıfırlamak için şifreyi girin"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Sonraki"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 958a3d2..cea870a4 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Більші елементи"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Найбільші елементи"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Спеціальний масштаб (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Довідка й відгуки"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Меню"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Введіть пароль, щоб скинути налаштування в демо-режимі"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Далі"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 31f0a33..0fb60d9 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"قدرے بڑا"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"سب سے بڑا"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"حسب ضرورت (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"مدد اور تاثرات"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"مینو"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"ڈیمو موڈ میں فیکٹری ری سیٹ کیلئے پاس ورڈ درج کریں"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"اگلا"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 7ff4588..4690db3 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Kattaroq"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Eng katta"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Moslashtirilgan (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Yordam va fikr-mulohaza"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menyu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Demo rejimda zavod holatiga qaytarish uchun parolni kiriting"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Keyingisi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index daa9335..937b37d 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Lớn hơn"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Lớn nhất"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Tùy chỉnh (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Trợ giúp và phản hồi"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Menu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Nhập mật khẩu để tiến hành khôi phục cài đặt gốc ở chế độ trình diễn"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Tiếp theo"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 6cb27e9..47753fd 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"较大"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自定义 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"帮助和反馈"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"菜单"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"输入密码即可在演示模式下恢复出厂设置"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"下一步"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 16039c1..4cbf5a6 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"較大"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自訂 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"說明和意見反映"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"選單"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"輸入密碼即可在示範模式下重設原廠設定"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"下一步"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index ed68638..cecc6ac 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"較大"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"最大"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"自訂 (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"說明與意見回饋"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"選單"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"如要在示範模式中恢復原廠設定,請輸入密碼"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"下一步"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 957584a..94c3abf 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -415,7 +415,6 @@
<string name="screen_zoom_summary_very_large" msgid="7108563375663670067">"Okukhulu kakhulu"</string>
<string name="screen_zoom_summary_extremely_large" msgid="7427320168263276227">"Okukhulu kakhulu"</string>
<string name="screen_zoom_summary_custom" msgid="5611979864124160447">"Ngokwezifiso (<xliff:g id="DENSITYDPI">%d</xliff:g>)"</string>
- <string name="help_feedback_label" msgid="6815040660801785649">"Usizo nempendulo"</string>
<string name="content_description_menu_button" msgid="8182594799812351266">"Imenyu"</string>
<string name="retail_demo_reset_message" msgid="118771671364131297">"Faka iphasiwedi ukuze wenze ukusetha kwefekthri kumodi yedemo"</string>
<string name="retail_demo_reset_next" msgid="8356731459226304963">"Okulandelayo"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 663b1f5..240a192 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -675,6 +675,7 @@
if (!mResumed) {
mResumed = true;
mSessionsChanged = true;
+ doPauseLocked();
doResumeIfNeededLocked();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 36d209e..7000f9d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -97,8 +97,6 @@
private PanProfile mPanProfile;
private PbapClientProfile mPbapClientProfile;
private PbapServerProfile mPbapProfile;
- private final boolean mUsePbapPce;
- private final boolean mUseMapClient;
private HearingAidProfile mHearingAidProfile;
/**
@@ -115,9 +113,6 @@
mDeviceManager = deviceManager;
mEventManager = eventManager;
- mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
- // MAP Client is typically used in the same situations as PBAP Client
- mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
// pass this reference to adapter and event manager (circular dependency)
adapter.setProfileManager(this);
@@ -130,17 +125,17 @@
void updateLocalProfiles() {
List<Integer> supportedList = BluetoothAdapter.getDefaultAdapter().getSupportedProfiles();
if (CollectionUtils.isEmpty(supportedList)) {
- if(DEBUG) Log.d(TAG, "supportedList is null");
+ if (DEBUG) Log.d(TAG, "supportedList is null");
return;
}
if (mA2dpProfile == null && supportedList.contains(BluetoothProfile.A2DP)) {
- if(DEBUG) Log.d(TAG, "Adding local A2DP profile");
+ if (DEBUG) Log.d(TAG, "Adding local A2DP profile");
mA2dpProfile = new A2dpProfile(mContext, mDeviceManager, this);
addProfile(mA2dpProfile, A2dpProfile.NAME,
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
}
if (mA2dpSinkProfile == null && supportedList.contains(BluetoothProfile.A2DP_SINK)) {
- if(DEBUG) Log.d(TAG, "Adding local A2DP SINK profile");
+ if (DEBUG) Log.d(TAG, "Adding local A2DP SINK profile");
mA2dpSinkProfile = new A2dpSinkProfile(mContext, mDeviceManager, this);
addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
@@ -154,66 +149,63 @@
BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
}
if (mHfpClientProfile == null && supportedList.contains(BluetoothProfile.HEADSET_CLIENT)) {
- if(DEBUG) Log.d(TAG, "Adding local HfpClient profile");
+ if (DEBUG) Log.d(TAG, "Adding local HfpClient profile");
mHfpClientProfile = new HfpClientProfile(mContext, mDeviceManager, this);
addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME,
BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED,
BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED,
BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
}
- if (mUseMapClient) {
- if (mMapClientProfile == null && supportedList.contains(BluetoothProfile.MAP_CLIENT)) {
- if(DEBUG) Log.d(TAG, "Adding local MAP CLIENT profile");
- mMapClientProfile =
- new MapClientProfile(mContext, mDeviceManager,this);
- addProfile(mMapClientProfile, MapClientProfile.NAME,
- BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
- }
- } else if (mMapProfile == null && supportedList.contains(BluetoothProfile.MAP)) {
- if(DEBUG) Log.d(TAG, "Adding local MAP profile");
+ if (mMapClientProfile == null && supportedList.contains(BluetoothProfile.MAP_CLIENT)) {
+ if (DEBUG) Log.d(TAG, "Adding local MAP CLIENT profile");
+ mMapClientProfile = new MapClientProfile(mContext, mDeviceManager,this);
+ addProfile(mMapClientProfile, MapClientProfile.NAME,
+ BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ if (mMapProfile == null && supportedList.contains(BluetoothProfile.MAP)) {
+ if (DEBUG) Log.d(TAG, "Adding local MAP profile");
mMapProfile = new MapProfile(mContext, mDeviceManager, this);
addProfile(mMapProfile, MapProfile.NAME, BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
}
if (mOppProfile == null && supportedList.contains(BluetoothProfile.OPP)) {
- if(DEBUG) Log.d(TAG, "Adding local OPP profile");
+ if (DEBUG) Log.d(TAG, "Adding local OPP profile");
mOppProfile = new OppProfile();
// Note: no event handler for OPP, only name map.
mProfileNameMap.put(OppProfile.NAME, mOppProfile);
}
if (mHearingAidProfile == null && supportedList.contains(BluetoothProfile.HEARING_AID)) {
- if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
+ if (DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
mHearingAidProfile = new HearingAidProfile(mContext, mDeviceManager,
this);
addProfile(mHearingAidProfile, HearingAidProfile.NAME,
BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
}
if (mHidProfile == null && supportedList.contains(BluetoothProfile.HID_HOST)) {
- if(DEBUG) Log.d(TAG, "Adding local HID_HOST profile");
+ if (DEBUG) Log.d(TAG, "Adding local HID_HOST profile");
mHidProfile = new HidProfile(mContext, mDeviceManager, this);
addProfile(mHidProfile, HidProfile.NAME,
BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
}
if (mHidDeviceProfile == null && supportedList.contains(BluetoothProfile.HID_DEVICE)) {
- if(DEBUG) Log.d(TAG, "Adding local HID_DEVICE profile");
+ if (DEBUG) Log.d(TAG, "Adding local HID_DEVICE profile");
mHidDeviceProfile = new HidDeviceProfile(mContext, mDeviceManager, this);
addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,
BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
}
if (mPanProfile == null && supportedList.contains(BluetoothProfile.PAN)) {
- if(DEBUG) Log.d(TAG, "Adding local PAN profile");
+ if (DEBUG) Log.d(TAG, "Adding local PAN profile");
mPanProfile = new PanProfile(mContext);
addPanProfile(mPanProfile, PanProfile.NAME,
BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
}
if (mPbapProfile == null && supportedList.contains(BluetoothProfile.PBAP)) {
- if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
+ if (DEBUG) Log.d(TAG, "Adding local PBAP profile");
mPbapProfile = new PbapServerProfile(mContext);
addProfile(mPbapProfile, PbapServerProfile.NAME,
BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
}
- if (mUsePbapPce && mPbapClientProfile == null && supportedList.contains(
- BluetoothProfile.PBAP_CLIENT)) {
- if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
+ if (mPbapClientProfile == null && supportedList.contains(BluetoothProfile.PBAP_CLIENT)) {
+ if (DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
mPbapClientProfile = new PbapClientProfile(mContext, mDeviceManager,this);
addProfile(mPbapClientProfile, PbapClientProfile.NAME,
BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
@@ -544,7 +536,9 @@
removedProfiles.remove(mMapClientProfile);
}
- if (mUsePbapPce) {
+ if ((mPbapClientProfile != null) &&
+ BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.PBAP_PCE) &&
+ BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) {
profiles.add(mPbapClientProfile);
removedProfiles.remove(mPbapClientProfile);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index 689669f..caea04f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -32,11 +32,10 @@
import java.util.List;
/**
- * MapProfile handles Bluetooth MAP profile.
+ * MapProfile handles the Bluetooth MAP MSE role
*/
public class MapProfile implements LocalBluetoothProfile {
private static final String TAG = "MapProfile";
- private static boolean V = true;
private BluetoothMap mService;
private boolean mIsProfileReady;
@@ -59,7 +58,7 @@
implements BluetoothProfile.ServiceListener {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
- if (V) Log.d(TAG,"Bluetooth service connected");
+ Log.d(TAG, "Bluetooth service connected");
mService = (BluetoothMap) proxy;
// We just bound to the service, so refresh the UI for any connected MAP devices.
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
@@ -81,14 +80,14 @@
}
public void onServiceDisconnected(int profile) {
- if (V) Log.d(TAG,"Bluetooth service disconnected");
+ Log.d(TAG, "Bluetooth service disconnected");
mProfileManager.callServiceDisconnectedListeners();
mIsProfileReady=false;
}
}
public boolean isProfileReady() {
- if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+ Log.d(TAG, "isProfileReady(): " + mIsProfileReady);
return mIsProfileReady;
}
@@ -114,45 +113,45 @@
}
public boolean connect(BluetoothDevice device) {
- if(V)Log.d(TAG,"connect() - should not get called");
+ Log.d(TAG, "connect() - should not get called");
return false; // MAP never connects out
}
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
- if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
- }
- return mService.disconnect(device);
- } else {
+ if (mService == null) {
return false;
}
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
}
public int getConnectionStatus(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
- if(V) Log.d(TAG,"getConnectionStatus: status is: "+ mService.getConnectionState(device));
-
- return !deviceList.isEmpty() && deviceList.get(0).equals(device)
- ? mService.getConnectionState(device)
- : BluetoothProfile.STATE_DISCONNECTED;
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
}
public boolean isPreferred(BluetoothDevice device) {
- if (mService == null) return false;
+ if (mService == null) {
+ return false;
+ }
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
}
public int getPreferred(BluetoothDevice device) {
- if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ if (mService == null) {
+ return BluetoothProfile.PRIORITY_OFF;
+ }
return mService.getPriority(device);
}
public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (mService == null) return;
+ if (mService == null) {
+ return;
+ }
if (preferred) {
if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
@@ -163,7 +162,9 @@
}
public List<BluetoothDevice> getConnectedDevices() {
- if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ if (mService == null) {
+ return new ArrayList<BluetoothDevice>(0);
+ }
return mService.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING,
@@ -201,7 +202,7 @@
}
protected void finalize() {
- if (V) Log.d(TAG, "finalize()");
+ Log.d(TAG, "finalize()");
if (mService != null) {
try {
BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP,
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 2213db8..274696b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -172,11 +172,13 @@
public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) {
Drawable badge = null;
- boolean isManaged = context.getSystemService(DevicePolicyManager.class)
- .getProfileOwnerAsUser(userId) != null;
- if (isManaged) {
- badge = getDrawableForDisplayDensity(
- context, com.android.internal.R.drawable.ic_corp_badge_case);
+ if (userId != UserHandle.USER_NULL) {
+ boolean isManaged = context.getSystemService(DevicePolicyManager.class)
+ .getProfileOwnerAsUser(userId) != null;
+ if (isManaged) {
+ badge = getDrawableForDisplayDensity(
+ context, com.android.internal.R.drawable.ic_corp_badge_case);
+ }
}
return setBadge(badge);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index 88be2b0..3e3c039 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -27,7 +27,6 @@
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.TtsSpan;
-import android.util.Log;
import com.android.settingslib.R;
@@ -37,8 +36,6 @@
/** Utility class for generally useful string methods **/
public class StringUtil {
- private static final String TAG = "StringUtil";
-
public static final int SECONDS_PER_MINUTE = 60;
public static final int SECONDS_PER_HOUR = 60 * 60;
public static final int SECONDS_PER_DAY = 24 * 60 * 60;
@@ -97,7 +94,6 @@
final Locale locale = context.getResources().getConfiguration().locale;
final MeasureFormat measureFormat = MeasureFormat.getInstance(
locale, FormatWidth.SHORT);
- Log.i(TAG, "Locale is: " + locale);
sb.append(measureFormat.formatMeasures(measureArray));
if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
@@ -150,7 +146,6 @@
null /* default NumberFormat */,
RelativeDateTimeFormatter.Style.LONG,
android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE);
- Log.i(TAG, "Locale is: " + locale);
return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit);
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index 3e6c3f2..044ec86 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -111,6 +111,7 @@
}
}
+ @GuardedBy("mLock")
private MemoryIntArray getBackingStoreLocked() {
if (mBackingStore == null) {
// One for the global table, two for system and secure tables for a
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 500199f..290a4f8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -722,6 +722,7 @@
}
}
+ @GuardedBy("mLock")
private void dumpForUserLocked(int userId, PrintWriter pw) {
if (userId == UserHandle.USER_SYSTEM) {
pw.println("GLOBAL SETTINGS (user " + userId + ")");
@@ -1179,6 +1180,7 @@
&& UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID;
}
+ @GuardedBy("mLock")
private Setting getSsaidSettingLocked(PackageInfo callingPkg, int owningUserId) {
// Get uid of caller (key) used to store ssaid value
String name = Integer.toString(
@@ -1709,6 +1711,7 @@
}
}
+ @GuardedBy("mLock")
private List<String> getSettingsNamesLocked(int settingsType, int userId) {
// Don't enforce the instant app whitelist for now -- its too prone to unintended breakage
// in the current form.
@@ -1801,6 +1804,7 @@
*
* @returns whether the enabled location providers changed.
*/
+ @GuardedBy("mLock")
private boolean updateLocationProvidersAllowedLocked(String value, String tag,
int owningUserId, boolean makeDefault, boolean forceNotify) {
if (TextUtils.isEmpty(value)) {
@@ -2718,6 +2722,7 @@
}
}
+ @GuardedBy("secureSettings.mLock")
private void ensureSecureSettingAndroidIdSetLocked(SettingsState secureSettings) {
Setting value = secureSettings.getSettingLocked(Settings.Secure.ANDROID_ID);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index e57483a..389d627 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -274,6 +274,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public int getVersionLocked() {
return mVersion;
}
@@ -283,6 +284,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public void setVersionLocked(int version) {
if (version == mVersion) {
return;
@@ -293,6 +295,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public void removeSettingsForPackageLocked(String packageName) {
boolean removedSomething = false;
@@ -317,6 +320,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public List<String> getSettingNamesLocked() {
ArrayList<String> names = new ArrayList<>();
final int settingsCount = mSettings.size();
@@ -328,6 +332,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public Setting getSettingLocked(String name) {
if (TextUtils.isEmpty(name)) {
return mNullSetting;
@@ -350,6 +355,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public void resetSettingDefaultValueLocked(String name) {
Setting oldSetting = getSettingLocked(name);
if (oldSetting != null && !oldSetting.isNull() && oldSetting.getDefaultValue() != null) {
@@ -366,6 +372,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public boolean insertSettingLocked(String name, String value, String tag,
boolean makeDefault, String packageName) {
if (TextUtils.isEmpty(name)) {
@@ -407,6 +414,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public boolean deleteSettingLocked(String name) {
if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) {
return false;
@@ -429,6 +437,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public boolean resetSettingLocked(String name) {
if (TextUtils.isEmpty(name) || !hasSettingLocked(name)) {
return false;
@@ -458,6 +467,7 @@
}
// The settings provider must hold its lock when calling here.
+ @GuardedBy("mLock")
public void destroyLocked(Runnable callback) {
mHandler.removeMessages(MyHandler.MSG_PERSIST_SETTINGS);
if (callback != null) {
@@ -471,6 +481,7 @@
}
}
+ @GuardedBy("mLock")
private void addHistoricalOperationLocked(String type, Setting setting) {
if (mHistoricalOperations == null) {
return;
@@ -553,6 +564,7 @@
}
}
+ @GuardedBy("mLock")
private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
String newValue, String oldDefaultValue, String newDefaultValue) {
if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
@@ -588,10 +600,12 @@
mPackageToMemoryUsage.put(packageName, newSize);
}
+ @GuardedBy("mLock")
private boolean hasSettingLocked(String name) {
return mSettings.indexOfKey(name) >= 0;
}
+ @GuardedBy("mLock")
private void scheduleWriteIfNeededLocked() {
// If dirty then we have a write already scheduled.
if (!mDirty) {
@@ -600,6 +614,7 @@
}
}
+ @GuardedBy("mLock")
private void writeStateAsyncLocked() {
final long currentTimeMillis = SystemClock.uptimeMillis();
@@ -771,6 +786,7 @@
}
}
+ @GuardedBy("mLock")
private void readStateSyncLocked() {
FileInputStream in;
try {
@@ -820,6 +836,7 @@
}
}
+ @GuardedBy("mLock")
private void parseSettingsLocked(XmlPullParser parser)
throws IOException, XmlPullParserException {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java
index c268d32..0cdb509a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java
@@ -29,7 +29,7 @@
super(context);
}
- public abstract void setIcon(State state);
+ public abstract void setIcon(State state, boolean allowAnimations);
public abstract void disableAnimation();
public abstract View getIconView();
}
diff --git a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
index 862b964a..90f46ce 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rBR/strings.xml
@@ -22,9 +22,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="3171996292755059205">"Bloqueio do teclado"</string>
<string name="keyguard_password_enter_pin_code" msgid="3420548423949593123">"Insira o código PIN"</string>
- <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"Digite o PUK do SIM e o novo código PIN."</string>
- <string name="keyguard_password_enter_puk_prompt" msgid="3747778500166059332">"Código PUK do SIM"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"Novo código PIN do SIM"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"Digite o PUK do chip e o novo código PIN."</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="3747778500166059332">"Código PUK do chip"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"Novo código PIN do chip"</string>
<string name="keyguard_password_entry_touch_hint" msgid="5790410752696806482"><font size="17">"Toque para inserir a senha"</font></string>
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Digite a senha para desbloquear"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Insira o PIN para desbloquear"</string>
@@ -40,20 +40,20 @@
<string name="keyguard_low_battery" msgid="9218432555787624490">"Conecte o seu carregador."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8566679946700751371">"Pressione Menu para desbloquear."</string>
<string name="keyguard_network_locked_message" msgid="6743537524631420759">"Rede bloqueada"</string>
- <string name="keyguard_missing_sim_message_short" msgid="6327533369959764518">"Sem cartão SIM"</string>
- <string name="keyguard_missing_sim_message" product="tablet" msgid="4550152848200783542">"Não há um cartão SIM no tablet."</string>
- <string name="keyguard_missing_sim_message" product="default" msgid="6585414237800161146">"Não há um cartão SIM no smartphone."</string>
- <string name="keyguard_missing_sim_instructions" msgid="7350295932015220392">"Insira um cartão SIM."</string>
- <string name="keyguard_missing_sim_instructions_long" msgid="589889372883904477">"O cartão SIM não foi inserido ou não é possível lê-lo. Insira um cartão SIM."</string>
- <string name="keyguard_permanent_disabled_sim_message_short" msgid="654102080186420706">"Cartão SIM inutilizável."</string>
- <string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"O cartão SIM foi desativado permanentemente.\nEntre em contato com seu provedor de serviços sem fio para receber outro cartão SIM."</string>
- <string name="keyguard_sim_locked_message" msgid="953766009432168127">"O cartão SIM está bloqueado."</string>
- <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"O cartão SIM está bloqueado pelo PUK."</string>
- <string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"Desbloqueando o cartão SIM…"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="6327533369959764518">"Sem chip"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="4550152848200783542">"Não há um chip no tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="6585414237800161146">"Não há um chip no smartphone."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7350295932015220392">"Insira um chip."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="589889372883904477">"O chip não foi inserido ou não é possível lê-lo. Insira um chip."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="654102080186420706">"Chip inutilizável."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"O chip foi desativado permanentemente.\nEntre em contato com seu provedor de serviços sem fio para receber outro chip."</string>
+ <string name="keyguard_sim_locked_message" msgid="953766009432168127">"O chip está bloqueado."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"O chip está bloqueado pelo PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"Desbloqueando o chip…"</string>
<string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"Área do PIN"</string>
<string name="keyguard_accessibility_password" msgid="7695303207740941101">"Senha do dispositivo"</string>
- <string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Área do PIN SIM"</string>
- <string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"Área do PUK SIM"</string>
+ <string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Área do PIN do chip"</string>
+ <string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"Área do PUK do chip"</string>
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Próximo alarme definido para <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Excluir"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Desativar eSIM"</string>
@@ -69,19 +69,19 @@
<item quantity="other">Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Desenhe seu padrão"</string>
- <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Informe o PIN do cartão SIM."</string>
- <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Informe o PIN do cartão SIM para \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
+ <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Informe o PIN do chip."</string>
+ <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Informe o PIN do chip para \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
<string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Desative o eSIM para usar o dispositivo sem serviço móvel."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Digite o PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Digite a senha"</string>
- <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"O SIM foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para mais detalhes."</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"O SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\" foi desativado. Informe o código PUK para continuar. Entre em contato com a operadora para saber mais detalhes."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"O chip foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para mais detalhes."</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"O chip \"<xliff:g id="CARRIER">%1$s</xliff:g>\" foi desativado. Informe o código PUK para continuar. Entre em contato com a operadora para saber mais detalhes."</string>
<string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"Digite o código PIN desejado"</string>
<string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"Confirme o código PIN desejado"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"Desbloqueando o cartão SIM…"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"Desbloqueando o chip…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"Digite um PIN com 4 a 8 números."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6003602401368264144">"O código PUK deve ter oito números ou mais."</string>
- <string name="kg_invalid_puk" msgid="5399287873762592502">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o SIM."</string>
+ <string name="kg_invalid_puk" msgid="5399287873762592502">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o chip."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"Os códigos PIN não coincidem"</string>
<string name="kg_login_too_many_attempts" msgid="6604574268387867255">"Muitas tentativas de padrão"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
@@ -101,18 +101,18 @@
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="8476407539834855">"Você tentou desbloquear o smartphone incorretamente <xliff:g id="NUMBER">%d</xliff:g> vezes. O perfil de trabalho será removido, o que excluirá todos os dados do perfil."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"Código PIN do SIM incorreto. Entre em contato com a operadora para desbloquear o dispositivo."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"Código PIN do chip incorreto. Entre em contato com a operadora para desbloquear o dispositivo."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
- <item quantity="one">Código PIN do SIM incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
- <item quantity="other">Código PIN do SIM incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
+ <item quantity="one">Código PIN do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
+ <item quantity="other">Código PIN do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
</plurals>
- <string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"O SIM está inutilizável. Entre em contato com a operadora."</string>
+ <string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"O chip não pode ser utilizado. Entre em contato com a operadora."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="2287504898931957513">
- <item quantity="one">Código PUK do SIM incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o SIM se tornará permanentemente inutilizável.</item>
- <item quantity="other">Código PUK do SIM incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o SIM se tornará permanentemente inutilizável.</item>
+ <item quantity="one">Código PUK do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip se tornará permanentemente inutilizável.</item>
+ <item quantity="other">Código PUK do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip se tornará permanentemente inutilizável.</item>
</plurals>
- <string name="kg_password_pin_failed" msgid="8769990811451236223">"Falha na operação de PIN do SIM."</string>
- <string name="kg_password_puk_failed" msgid="1331621440873439974">"Falha na operação de PUK do SIM."</string>
+ <string name="kg_password_pin_failed" msgid="8769990811451236223">"Falha na operação de PIN do chip."</string>
+ <string name="kg_password_puk_failed" msgid="1331621440873439974">"Falha na operação de PUK do chip."</string>
<string name="kg_pin_accepted" msgid="7637293533973802143">"Código aceito."</string>
<string name="keyguard_carrier_default" msgid="4274828292998453695">"Sem serviço."</string>
<string name="accessibility_ime_switch_button" msgid="2695096475319405612">"Alterar o método de entrada"</string>
@@ -143,11 +143,11 @@
<string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Não reconhecido"</string>
<string name="kg_face_not_recognized" msgid="6382535088345875294">"Não reconhecido"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
- <item quantity="one">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
- <item quantity="other">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
+ <item quantity="one">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
+ <item quantity="other">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
- <item quantity="one">O SIM agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o SIM se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
- <item quantity="other">O SIM agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o SIM se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
+ <item quantity="one">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
+ <item quantity="other">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values-pt/strings.xml b/packages/SystemUI/res-keyguard/values-pt/strings.xml
index 862b964a..90f46ce 100644
--- a/packages/SystemUI/res-keyguard/values-pt/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt/strings.xml
@@ -22,9 +22,9 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="3171996292755059205">"Bloqueio do teclado"</string>
<string name="keyguard_password_enter_pin_code" msgid="3420548423949593123">"Insira o código PIN"</string>
- <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"Digite o PUK do SIM e o novo código PIN."</string>
- <string name="keyguard_password_enter_puk_prompt" msgid="3747778500166059332">"Código PUK do SIM"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"Novo código PIN do SIM"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"Digite o PUK do chip e o novo código PIN."</string>
+ <string name="keyguard_password_enter_puk_prompt" msgid="3747778500166059332">"Código PUK do chip"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"Novo código PIN do chip"</string>
<string name="keyguard_password_entry_touch_hint" msgid="5790410752696806482"><font size="17">"Toque para inserir a senha"</font></string>
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Digite a senha para desbloquear"</string>
<string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Insira o PIN para desbloquear"</string>
@@ -40,20 +40,20 @@
<string name="keyguard_low_battery" msgid="9218432555787624490">"Conecte o seu carregador."</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="8566679946700751371">"Pressione Menu para desbloquear."</string>
<string name="keyguard_network_locked_message" msgid="6743537524631420759">"Rede bloqueada"</string>
- <string name="keyguard_missing_sim_message_short" msgid="6327533369959764518">"Sem cartão SIM"</string>
- <string name="keyguard_missing_sim_message" product="tablet" msgid="4550152848200783542">"Não há um cartão SIM no tablet."</string>
- <string name="keyguard_missing_sim_message" product="default" msgid="6585414237800161146">"Não há um cartão SIM no smartphone."</string>
- <string name="keyguard_missing_sim_instructions" msgid="7350295932015220392">"Insira um cartão SIM."</string>
- <string name="keyguard_missing_sim_instructions_long" msgid="589889372883904477">"O cartão SIM não foi inserido ou não é possível lê-lo. Insira um cartão SIM."</string>
- <string name="keyguard_permanent_disabled_sim_message_short" msgid="654102080186420706">"Cartão SIM inutilizável."</string>
- <string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"O cartão SIM foi desativado permanentemente.\nEntre em contato com seu provedor de serviços sem fio para receber outro cartão SIM."</string>
- <string name="keyguard_sim_locked_message" msgid="953766009432168127">"O cartão SIM está bloqueado."</string>
- <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"O cartão SIM está bloqueado pelo PUK."</string>
- <string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"Desbloqueando o cartão SIM…"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="6327533369959764518">"Sem chip"</string>
+ <string name="keyguard_missing_sim_message" product="tablet" msgid="4550152848200783542">"Não há um chip no tablet."</string>
+ <string name="keyguard_missing_sim_message" product="default" msgid="6585414237800161146">"Não há um chip no smartphone."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="7350295932015220392">"Insira um chip."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="589889372883904477">"O chip não foi inserido ou não é possível lê-lo. Insira um chip."</string>
+ <string name="keyguard_permanent_disabled_sim_message_short" msgid="654102080186420706">"Chip inutilizável."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="4683178224791318347">"O chip foi desativado permanentemente.\nEntre em contato com seu provedor de serviços sem fio para receber outro chip."</string>
+ <string name="keyguard_sim_locked_message" msgid="953766009432168127">"O chip está bloqueado."</string>
+ <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"O chip está bloqueado pelo PUK."</string>
+ <string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"Desbloqueando o chip…"</string>
<string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"Área do PIN"</string>
<string name="keyguard_accessibility_password" msgid="7695303207740941101">"Senha do dispositivo"</string>
- <string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Área do PIN SIM"</string>
- <string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"Área do PUK SIM"</string>
+ <string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Área do PIN do chip"</string>
+ <string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"Área do PUK do chip"</string>
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Próximo alarme definido para <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Excluir"</string>
<string name="disable_carrier_button_text" msgid="6914341927421916114">"Desativar eSIM"</string>
@@ -69,19 +69,19 @@
<item quantity="other">Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Desenhe seu padrão"</string>
- <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Informe o PIN do cartão SIM."</string>
- <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Informe o PIN do cartão SIM para \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
+ <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Informe o PIN do chip."</string>
+ <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Informe o PIN do chip para \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
<string name="kg_sim_lock_esim_instructions" msgid="4416732549172148542">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Desative o eSIM para usar o dispositivo sem serviço móvel."</string>
<string name="kg_pin_instructions" msgid="4069609316644030034">"Digite o PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Digite a senha"</string>
- <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"O SIM foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para mais detalhes."</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"O SIM \"<xliff:g id="CARRIER">%1$s</xliff:g>\" foi desativado. Informe o código PUK para continuar. Entre em contato com a operadora para saber mais detalhes."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"O chip foi desativado. Insira o código PUK para continuar. Entre em contato com a operadora para mais detalhes."</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"O chip \"<xliff:g id="CARRIER">%1$s</xliff:g>\" foi desativado. Informe o código PUK para continuar. Entre em contato com a operadora para saber mais detalhes."</string>
<string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"Digite o código PIN desejado"</string>
<string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"Confirme o código PIN desejado"</string>
- <string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"Desbloqueando o cartão SIM…"</string>
+ <string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"Desbloqueando o chip…"</string>
<string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"Digite um PIN com 4 a 8 números."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6003602401368264144">"O código PUK deve ter oito números ou mais."</string>
- <string name="kg_invalid_puk" msgid="5399287873762592502">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o SIM."</string>
+ <string name="kg_invalid_puk" msgid="5399287873762592502">"Introduza novamente o código PUK correto. Muitas tentativas malsucedidas desativarão permanentemente o chip."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"Os códigos PIN não coincidem"</string>
<string name="kg_login_too_many_attempts" msgid="6604574268387867255">"Muitas tentativas de padrão"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"Você digitou seu PIN incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. \n\nTente novamente em <xliff:g id="NUMBER_1">%2$d</xliff:g> segundos."</string>
@@ -101,18 +101,18 @@
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="8476407539834855">"Você tentou desbloquear o smartphone incorretamente <xliff:g id="NUMBER">%d</xliff:g> vezes. O perfil de trabalho será removido, o que excluirá todos os dados do perfil."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear seu tablet.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"Você desenhou sua sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Se fizer mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas incorretas, será solicitado que você use o login do Google para desbloquear.\n\n Tente novamente em <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"Código PIN do SIM incorreto. Entre em contato com a operadora para desbloquear o dispositivo."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"Código PIN do chip incorreto. Entre em contato com a operadora para desbloquear o dispositivo."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
- <item quantity="one">Código PIN do SIM incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
- <item quantity="other">Código PIN do SIM incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
+ <item quantity="one">Código PIN do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
+ <item quantity="other">Código PIN do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>.</item>
</plurals>
- <string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"O SIM está inutilizável. Entre em contato com a operadora."</string>
+ <string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"O chip não pode ser utilizado. Entre em contato com a operadora."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="2287504898931957513">
- <item quantity="one">Código PUK do SIM incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o SIM se tornará permanentemente inutilizável.</item>
- <item quantity="other">Código PUK do SIM incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o SIM se tornará permanentemente inutilizável.</item>
+ <item quantity="one">Código PUK do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip se tornará permanentemente inutilizável.</item>
+ <item quantity="other">Código PUK do chip incorreto. Tentativas restantes: <xliff:g id="NUMBER_1">%d</xliff:g>. Caso o código correto não seja digitado, o chip se tornará permanentemente inutilizável.</item>
</plurals>
- <string name="kg_password_pin_failed" msgid="8769990811451236223">"Falha na operação de PIN do SIM."</string>
- <string name="kg_password_puk_failed" msgid="1331621440873439974">"Falha na operação de PUK do SIM."</string>
+ <string name="kg_password_pin_failed" msgid="8769990811451236223">"Falha na operação de PIN do chip."</string>
+ <string name="kg_password_puk_failed" msgid="1331621440873439974">"Falha na operação de PUK do chip."</string>
<string name="kg_pin_accepted" msgid="7637293533973802143">"Código aceito."</string>
<string name="keyguard_carrier_default" msgid="4274828292998453695">"Sem serviço."</string>
<string name="accessibility_ime_switch_button" msgid="2695096475319405612">"Alterar o método de entrada"</string>
@@ -143,11 +143,11 @@
<string name="kg_fingerprint_not_recognized" msgid="7854413849848459418">"Não reconhecido"</string>
<string name="kg_face_not_recognized" msgid="6382535088345875294">"Não reconhecido"</string>
<plurals name="kg_password_default_pin_message" formatted="false" msgid="3739658416797652781">
- <item quantity="one">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
- <item quantity="other">Informe o PIN do SIM. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
+ <item quantity="one">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativa restante.</item>
+ <item quantity="other">Informe o PIN do chip. Você tem <xliff:g id="NUMBER_1">%d</xliff:g> tentativas restantes.</item>
</plurals>
<plurals name="kg_password_default_puk_message" formatted="false" msgid="8744416410184198352">
- <item quantity="one">O SIM agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o SIM se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
- <item quantity="other">O SIM agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o SIM se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
+ <item quantity="one">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativa restante antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
+ <item quantity="other">O chip agora está desativado. Informe o código PUK para continuar. Você tem <xliff:g id="_NUMBER_1">%d</xliff:g> tentativas restantes antes de o chip se tornar permanentemente inutilizável. Entre em contato com a operadora para saber mais detalhes.</item>
</plurals>
</resources>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index e1e812b..7a38899 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -154,6 +154,7 @@
style="@style/TextAppearance.NotificationInfo.Button" />
<TextView
android:id="@+id/keep"
+ android:minWidth="48dp"
android:text="@string/inline_keep_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 0892f73..967e13f 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -38,7 +38,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:gravity="center_vertical"
- android:visibility="invisible">
+ android:alpha="0">
<ImageView
android:id="@+id/next_alarm_icon"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 6bfd9b2..44691c2 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"MELD GEBRUIKER AF"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Voeg nuwe gebruiker by?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Wanneer jy \'n nuwe gebruiker byvoeg, moet daardie persoon hul spasie opstel.\n\nEnige gebruiker kan programme vir al die ander gebruikers opdateer."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Gebruikerlimiet is bereik"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Jy kan tot <xliff:g id="COUNT">%d</xliff:g> gebruikers byvoeg.</item>
+ <item quantity="one">Net een gebruiker kan geskep word.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Verwyder gebruiker?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Alle programme en data van hierdie gebruiker sal uitgevee word."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Verwyder"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Links-ikoon"</string>
<string name="right_icon" msgid="3952104823293824311">"Regs-ikoon"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Hou en sleep om teëls by te voeg"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Hou en sleep om teëls te herrangskik"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Sleep hierheen om te verwyder"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Jy moet minstens 6 teëls hê"</string>
<string name="qs_edit" msgid="2232596095725105230">"Wysig"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 43a27f0..966e4d7 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ተጠቃሚን አስወጣ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"አዲስ ተጠቃሚ ይታከል?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"እርስዎ አንድ አዲስ ተጠቃሚ ሲያክሉ ያ ሰው የራሱ ቦታ ማዘጋጀት አለበት።\n\nማንኛውም ተጠቃሚ መተግበሪያዎችን ለሌሎች ተጠቃሚዎች ሁሉ ሊያዘምን ይችላል።"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"የተጠቃሚ ገደብ ላይ ተደርሷል"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> ተጠቃሚዎች ብቻ ናቸው ሊፈጠሩ የሚችሉት።</item>
+ <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> ተጠቃሚዎች ብቻ ናቸው ሊፈጠሩ የሚችሉት።</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ተጠቃሚ ይወገድ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ሁሉም የዚህ ተጠቃሚ መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"አስወግድ"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"የግራ አዶ"</string>
<string name="right_icon" msgid="3952104823293824311">"የቀኝ አዶ"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ፋይሎችን ለማከል ይዘት ይጎትቱ"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ሰድሮችን ዳግም ለማስተካከል ይያዙ እና ይጎትቱ።"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ለማስወገድ ወደዚህ ይጎትቱ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"ቢያንስ 6 ሰቆች ያስፈልገዎታል"</string>
<string name="qs_edit" msgid="2232596095725105230">"አርትዕ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 598d822..278b00a 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -438,9 +438,15 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"خروج المستخدم"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"هل تريد إضافة مستخدم جديد؟"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"عند إضافة مستخدم جديد، عليه إعداد مساحته.\n\nويُمكن لأي مستخدم تحديث التطبيقات لجميع المستخدمين الآخرين."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"تم الوصول إلى أقصى عدد للمستخدمين"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="zero">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدم.</item>
+ <item quantity="two">يمكنك إضافة ما يصل إلى مستخدمينِ (<xliff:g id="COUNT">%d</xliff:g>).</item>
+ <item quantity="few">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدمين.</item>
+ <item quantity="many">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدمًا.</item>
+ <item quantity="other">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدم.</item>
+ <item quantity="one">يمكن إنشاء مستخدم واحد فقط.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"هل تريد إزالة المستخدم؟"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"سيتم حذف جميع تطبيقات وبيانات هذا المستخدم."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"إزالة"</string>
@@ -752,8 +758,7 @@
<string name="left_icon" msgid="3096287125959387541">"رمز اليسار"</string>
<string name="right_icon" msgid="3952104823293824311">"رمز اليمين"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"انقر باستمرار مع السحب لإضافة المربعات."</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"اضغط باستمرار مع السحب لإعادة ترتيب المربّعات."</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"اسحب هنا للإزالة"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"تحتاج إلى 6 مربعات على الأقل."</string>
<string name="qs_edit" msgid="2232596095725105230">"تعديل"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 84d178b..8912f15 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ব্যৱহাৰকাৰীক লগ আউট কৰক"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"নতুন ব্যৱহাৰকাৰী যোগ কৰিবনে?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"আপুনি যেতিয়া এজন নতুন ব্যৱহাৰকাৰী যোগ কৰে, তেওঁ নিজৰ স্থান ছেট আপ কৰা প্ৰয়োজন।\n\nযিকোনো ব্যৱহাৰকাৰীয়ে নিজৰ লগতে আন ব্যৱহাৰকাৰীৰো এপ্ আপডেট কৰিব পাৰে।"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"অধিকতম ব্যৱহাৰকাৰী সৃষ্টি কৰা হ’ল"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">আপুনি <xliff:g id="COUNT">%d</xliff:g> জনলৈকে ব্যৱহাৰকাৰী যোগ কৰিব পাৰে।</item>
+ <item quantity="other">আপুনি <xliff:g id="COUNT">%d</xliff:g> জনলৈকে ব্যৱহাৰকাৰী যোগ কৰিব পাৰে।</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ব্যৱহাৰকাৰীক আঁতৰাবনে?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"এই ব্যৱহাৰকাৰীৰ সকলো এপ্ আৰু ডেটা মচা হ\'ব।"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"আঁতৰাওক"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"বাওঁ আইকন"</string>
<string name="right_icon" msgid="3952104823293824311">"সোঁ আইকন"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"টাইল যোগ কৰিবলৈ হেঁচি ধৰি টানি আনক"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"টাইলসমূহ পুনৰ সজাবলৈ ধৰি টানক"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"আঁতৰাবৰ বাবে টানি আনি ইয়াত এৰি দিয়ক"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"আপোনাক অতি কমেও ৬খন টাইল লাগিব"</string>
<string name="qs_edit" msgid="2232596095725105230">"সম্পাদনা কৰক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index bbc67a5..f0779fb 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -209,7 +209,7 @@
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Təyyarə rejimi deaktiv edildi."</string>
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Təyyarə rejimi aktiv edildi."</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"tam sakitlik"</string>
- <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"yalnız siqnallar"</string>
+ <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3357131899365865386">"yalnız alarmlar"</string>
<string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"Narahat etməyin."</string>
<string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"\"Narahat etməyin\" qeyri-aktivdir."</string>
<string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"\"Narahat etməyin\" aktivdir."</string>
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"İSTİFADƏÇİ ÇIXIŞI"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Yeni istifadəçi əlavə edilsin?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Yeni istifadəçi əlavə etdiyiniz zaman həmin şəxs öz yerini quraşdırmalıdır. \n\n İstənilən istifadəçi bütün digər istifadəçilərdən olan tətbiqləri güncəlləşdirə bilər."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"İstifadəçi limitinə çatmısınız"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Maksimum <xliff:g id="COUNT">%d</xliff:g> istifadəçi əlavə edə bilərsiniz.</item>
+ <item quantity="one">Yalnız bir istifadəçi yaradıla bilər.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"İstifadəçi silinsin?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Bu istifadəçinin bütün tətbiqləri və datası silinəcək."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Silin"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Sol ikona"</string>
<string name="right_icon" msgid="3952104823293824311">"Sağ ikona"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Mozaika əlavə etmək üçün basıb saxlayaraq çəkin"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Mozaikaları yenidən təşkil etmək üçün basıb saxlayın və çəkin"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Silmək üçün bura sürüşdürün"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Minimum 6 mozaikaya ehtiyacınız var"</string>
<string name="qs_edit" msgid="2232596095725105230">"Redaktə edin"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 679fdde..9dc6460 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -429,9 +429,12 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ODJAVI KORISNIKA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Dodajete novog korisnika?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kada dodate novog korisnika, ta osoba treba da podesi svoj prostor.\n\nSvaki korisnik može da ažurira aplikacije za sve ostale korisnike."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Dostignut maksimalni broj korisnika"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Možete da dodate najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ <item quantity="few">Možete da dodate najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ <item quantity="other">Možete da dodate najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Želite li da uklonite korisnika?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Sve aplikacije i podaci ovog korisnika će biti izbrisani."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Ukloni"</string>
@@ -737,8 +740,7 @@
<string name="left_icon" msgid="3096287125959387541">"Leva ikona"</string>
<string name="right_icon" msgid="3952104823293824311">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Zadržite i prevucite da biste dodali pločice"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Zadržite i prevucite da biste promenili raspored pločica"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Prevucite ovde da biste uklonili"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Treba da izaberete najmanje 6 pločica"</string>
<string name="qs_edit" msgid="2232596095725105230">"Izmeni"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index d26b322..9898dbd 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -434,9 +434,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ВЫКАНАЦЬ ВЫХАД КАРЫСТАЛЬНІКА"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Дадаць новага карыстальніка?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Пасля стварэння профіля яго трэба наладзіць.\n\nЛюбы карыстальнік прылады можа абнаўляць праграмы ўсіх іншых карыстальнікаў."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Дасягнуты ліміт карыстальнікаў"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальніка.</item>
+ <item quantity="few">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальнікаў.</item>
+ <item quantity="many">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальнікаў.</item>
+ <item quantity="other">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальніка.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Выдаліць карыстальніка?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Усе праграмы і даныя гэтага карыстальніка будуць выдалены."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Выдаліць"</string>
@@ -744,8 +748,7 @@
<string name="left_icon" msgid="3096287125959387541">"Значок \"улева\""</string>
<string name="right_icon" msgid="3952104823293824311">"Значок \"управа\""</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Перацягніце патрэбныя пліткі"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Каб змяніць парадак плітак, утрымлівайце і перацягвайце іх"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Перацягніце сюды, каб выдаліць"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Трэба па меншай меры 6 плітак"</string>
<string name="qs_edit" msgid="2232596095725105230">"Рэдагаваць"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 2bd6cc4..a5506a3 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ИЗЛИЗАНЕ НА ПОТРЕБИТЕЛЯ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Да се добави ли нов потреб.?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Когато добавите нов потребител, той трябва да настрои работното си пространство.\n\nВсеки потребител може да актуализира приложенията за всички останали потребители."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Достигнахте огранич. за потребители"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Можете да добавите до <xliff:g id="COUNT">%d</xliff:g> потребители.</item>
+ <item quantity="one">Може да бъде създаден само един потребител.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Да се премахне ли потребителят?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Всички приложения и данни на този потребител ще бъдат изтрити."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Премахване"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Лява икона"</string>
<string name="right_icon" msgid="3952104823293824311">"Дясна икона"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Задръжте и плъзнете, за да добавите плочки"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Задръжте и плъзнете, за да пренаредите плочките"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Преместете тук с плъзгане за премахване"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Трябва да останат поне 6 плочки"</string>
<string name="qs_edit" msgid="2232596095725105230">"Редактиране"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 866deae..5d52103 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ব্যবহারকারীকে লগ-আউট করুন"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"নতুন ব্যবহারকারীকে যোগ করবেন?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"আপনি একজন নতুন ব্যবহারকারী যোগ করলে তাকে তার জায়গা সেট আপ করে নিতে হবে৷\n\nযেকোনো ব্যবহারকারী অন্য সব ব্যবহারকারীর জন্য অ্যাপ্লিকেশান আপডেট করতে পারবেন৷"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"আর কোনও প্রোফাইল যোগ করা যাবে না"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">আপনি <xliff:g id="COUNT">%d</xliff:g> জন পর্যন্ত ব্যবহারকারীর প্রোফাইল যোগ করতে পারেন।</item>
+ <item quantity="other">আপনি <xliff:g id="COUNT">%d</xliff:g> জন পর্যন্ত ব্যবহারকারীর প্রোফাইল যোগ করতে পারেন।</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ব্যবহারকারী সরাবেন?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"এই ব্যবহারকারীর সমস্ত অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"সরান"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"বাঁ দিকের আইকন"</string>
<string name="right_icon" msgid="3952104823293824311">"ডানদিকের আইকন"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"টাইল যোগ করতে ট্যাপ করে টেনে আনুন"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"টাইলগুলি আবার সাজানোর জন্য ধরে থেকে টেনে আনুন"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"সরানোর জন্য এখানে টেনে আনুন"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"অন্তত ৬টি টাইল রাখতে হবে"</string>
<string name="qs_edit" msgid="2232596095725105230">"সম্পাদনা করুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 57089b8..4527e9a 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -429,9 +429,12 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ODJAVI KORISNIKA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Želite dodati novog korisnika?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kada dodate novog korisnika, ta osoba treba uspostaviti svoj prostor.\n\nSvaki korisnik može ažurirati aplikacije za sve ostale korisnike."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Dostignut limit za broj korisnika"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ <item quantity="few">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ <item quantity="other">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Zaista želite ukloniti korisnika?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Sve aplikacije i podaci ovog korisnika bit će izbrisani."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Ukloni"</string>
@@ -739,8 +742,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ikona lijevo"</string>
<string name="right_icon" msgid="3952104823293824311">"Ikona desno"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Držite i prevucite da dodate polja"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Držite i prevucite da preuredite polja"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Prevucite ovdje za uklanjanje"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Trebate najmanje šest polja"</string>
<string name="qs_edit" msgid="2232596095725105230">"Uredi"</string>
@@ -851,7 +853,7 @@
<string name="touch_filtered_warning" msgid="8671693809204767551">"Postavke ne mogu potvrditi vaš odgovor jer aplikacija zaklanja zahtjev za odobrenje."</string>
<string name="slice_permission_title" msgid="7465009437851044444">"Dozvoliti aplikaciji <xliff:g id="APP_0">%1$s</xliff:g> prikazivanje isječaka aplikacije <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="3514586565609596523">"- Može čitati informacije iz aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
- <string name="slice_permission_text_2" msgid="3146758297471143723">"- Može poduzeti radnje unutar aplikacije <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="slice_permission_text_2" msgid="3146758297471143723">"- Može poduzeti radnje u aplikaciji <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="slice_permission_checkbox" msgid="7986504458640562900">"Dozvoli aplikaciji <xliff:g id="APP">%1$s</xliff:g> prikazivanje isječaka iz svake aplikacije"</string>
<string name="slice_permission_allow" msgid="2340244901366722709">"Dozvoli"</string>
<string name="slice_permission_deny" msgid="7683681514008048807">"Odbij"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 920db25..23754ea 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"TANCA LA SESSIÓ DE L\'USUARI"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Vols afegir un usuari nou?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Quan s\'afegeix un usuari nou, aquest usuari ha de configurar-se l\'espai.\n\nQualsevol usuari pot actualitzar les aplicacions de la resta d\'usuaris."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"S\'ha assolit el límit d\'usuaris"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Pots afegir fins a <xliff:g id="COUNT">%d</xliff:g> usuaris.</item>
+ <item quantity="one">Només es pot crear un usuari.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Vols suprimir l\'usuari?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Totes les aplicacions i les dades d\'aquest usuari se suprimiran."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Suprimeix"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Icona de l\'esquerra"</string>
<string name="right_icon" msgid="3952104823293824311">"Icona de la dreta"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Mantén premut i arrossega per afegir funcions"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Mantén premut i arrossega per reorganitzar les funcions"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arrossega aquí per suprimir una funció"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Necessites com a mínim 6 mosaics"</string>
<string name="qs_edit" msgid="2232596095725105230">"Edita"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 4077908..e7583b5 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -434,9 +434,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ODHLÁSIT UŽIVATELE"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Přidat nového uživatele?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Když přidáte nového uživatele, musí si nastavit vlastní prostor.\n\nJakýkoli uživatel může aktualizovat aplikace všech ostatních uživatelů."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Bylo dosaženo limitu uživatelů"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="few">Lze přidat až <xliff:g id="COUNT">%d</xliff:g> uživatele.</item>
+ <item quantity="many">Lze přidat až <xliff:g id="COUNT">%d</xliff:g> uživatele.</item>
+ <item quantity="other">Lze přidat až <xliff:g id="COUNT">%d</xliff:g> uživatelů.</item>
+ <item quantity="one">Lze vytvořit jen jednoho uživatele.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Odstranit uživatele?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Veškeré aplikace a data tohoto uživatele budou smazána."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Odstranit"</string>
@@ -744,8 +748,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ikona vlevo"</string>
<string name="right_icon" msgid="3952104823293824311">"Ikona vpravo"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Dlaždice přidáte podržením a přetažením"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Dlaždice můžete uspořádat podržením a přetažením"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Přetažením sem dlaždice odstraníte"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Potřebujete minimálně šest dlaždic"</string>
<string name="qs_edit" msgid="2232596095725105230">"Upravit"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 2ebc0e8..31634a9 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"LOG BRUGEREN UD"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Vil du tilføje den nye bruger?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Når du tilføjer en ny bruger, skal personen konfigurere sit område.\n\nEnhver bruger kan opdatere apps for alle andre brugere."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Grænsen for antal brugere er nået"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Du kan tilføje op til <xliff:g id="COUNT">%d</xliff:g> bruger.</item>
+ <item quantity="other">Du kan tilføje op til <xliff:g id="COUNT">%d</xliff:g> brugere.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Vil du fjerne brugeren?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Alle apps og data for denne bruger slettes."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Fjern"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Venstre ikon"</string>
<string name="right_icon" msgid="3952104823293824311">"Højre ikon"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Tilføj felter ved at holde dem nede og trække"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Omorganiser felter ved at holde dem nede og trække"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Træk herhen for at fjerne"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Du skal bruge mindst seks felter"</string>
<string name="qs_edit" msgid="2232596095725105230">"Rediger"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 6636c9d..aa6e449 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -430,9 +430,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"Nutzer abmelden"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Neuen Nutzer hinzufügen?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Wenn du einen neuen Nutzer hinzufügst, muss dieser seinen Bereich einrichten.\n\nJeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Nutzerlimit erreicht"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Du kannst bis zu <xliff:g id="COUNT">%d</xliff:g> Nutzer hinzufügen.</item>
+ <item quantity="one">Es kann nur ein Nutzer erstellt werden.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Nutzer entfernen?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Entfernen"</string>
@@ -736,8 +738,7 @@
<string name="left_icon" msgid="3096287125959387541">"Linkes Symbol"</string>
<string name="right_icon" msgid="3952104823293824311">"Rechtes Symbol"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Halten und ziehen, um Kacheln hinzuzufügen"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Halten und ziehen, um die Kacheln neu anzuordnen"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Zum Entfernen hierher ziehen"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Du brauchst mindestens sechs Kacheln"</string>
<string name="qs_edit" msgid="2232596095725105230">"Bearbeiten"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 60c412a..1a1c762 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ΑΠΟΣΥΝΔΕΣΗ ΧΡΗΣΤΗ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Προσθήκη νέου χρήστη;"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Κατά την προσθήκη ενός νέου χρήστη, αυτός θα πρέπει να ρυθμίσει το χώρο του.\n\nΟποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Συμπληρώθηκε το όριο χρηστών"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Μπορείτε να προσθέσετε έως <xliff:g id="COUNT">%d</xliff:g> χρήστες.</item>
+ <item quantity="one">Είναι δυνατή η δημιουργία μόνο ενός χρήστη.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Κατάργηση χρήστη;"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Όλες οι εφαρμογές και τα δεδομένα αυτού του χρήστη θα διαγραφούν."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Κατάργηση"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Αριστερό εικονίδιο"</string>
<string name="right_icon" msgid="3952104823293824311">"Δεξιό εικονίδιο"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Κρατήστε και σύρετε για την προσθήκη πλακιδίων"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Κρατήστε και σύρετε για αναδιάταξη των πλακιδίων"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Σύρετε εδώ για κατάργηση"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Απαιτούνται τουλάχιστον 6 πλακίδια"</string>
<string name="qs_edit" msgid="2232596095725105230">"Επεξεργασία"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index f5587fe..31509dc 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"SALIR DE SESIÓN DEL USUARIO"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"¿Agregar usuario nuevo?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Cuando agregas un nuevo usuario, esa persona debe configurar su espacio.\n\nCualquier usuario puede actualizar las aplicaciones del resto de los usuarios."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Alcanzaste el límite de usuarios"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Puedes agregar hasta <xliff:g id="COUNT">%d</xliff:g> usuarios.</item>
+ <item quantity="one">Solo se puede crear un usuario.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"¿Confirmas que quieres quitar el usuario?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Se borrarán todas las aplicaciones y los datos de este usuario."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Quitar"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ícono izquierdo"</string>
<string name="right_icon" msgid="3952104823293824311">"Ícono derecho"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Mantén presionado para agregar mosaicos"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Mantén presionado y arrastra para reorganizar los mosaicos"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arrastra aquí para quitar"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Necesitas al menos 6 mosaicos"</string>
<string name="qs_edit" msgid="2232596095725105230">"Editar"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index e78e130..2cc3abd 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"SALIR DE USUARIO"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"¿Añadir nuevo usuario?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Al añadir un nuevo usuario, este debe configurar su espacio.\n\nCualquier usuario puede actualizar las aplicaciones del resto de usuarios."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Has alcanzado el límite de usuarios"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Puedes añadir hasta <xliff:g id="COUNT">%d</xliff:g> usuarios.</item>
+ <item quantity="one">Solo se puede crear un usuario.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"¿Quitar usuario?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Se eliminarán todas las aplicaciones y todos los datos de este usuario."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Quitar"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Icono a la izquierda"</string>
<string name="right_icon" msgid="3952104823293824311">"Icono a la derecha"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Pulsa y arrastra para añadir funciones"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Mantener pulsado y arrastrar para reorganizar los mosaicos"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arrastra aquí para quitar una función"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Necesitas 6 mosaicos como mínimo"</string>
<string name="qs_edit" msgid="2232596095725105230">"Editar"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 68cb738..f22dc1a 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"LOGI KASUTAJA VÄLJA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Kas lisada uus kasutaja?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kui lisate uue kasutaja, siis peab ta seadistama oma ruumi.\n\nIga kasutaja saab värskendada rakendusi kõigi kasutajate jaoks."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Kasutajate limiit on täis"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Võite lisada kuni <xliff:g id="COUNT">%d</xliff:g> kasutajat.</item>
+ <item quantity="one">Luua saab ainult ühe kasutaja.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Kas eemaldada kasutaja?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Kasutaja kõik rakendused ja andmed kustutatakse."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Eemalda"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Vasak ikoon"</string>
<string name="right_icon" msgid="3952104823293824311">"Parem ikoon"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Paanide lisamiseks hoidke all ja lohistage"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Paanide ümberpaigutamiseks hoidke neid all ja lohistage"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Lohistage eemaldamiseks siia"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Teil on vaja vähemalt kuut paani"</string>
<string name="qs_edit" msgid="2232596095725105230">"Muutmine"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index e203b76..87dba92 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"AMAITU ERABILTZAILEAREN SAIOA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Beste erabiltzaile bat gehitu?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Erabiltzaile bat gehitzen duzunean, horrek bere eremua konfiguratu beharko du.\n\nEdozein erabiltzailek egunera ditzake beste erabiltzaile guztien aplikazioak."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Erabiltzaile-mugara iritsi zara"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Gehienez, <xliff:g id="COUNT">%d</xliff:g> erabiltzaile gehi ditzakezu.</item>
+ <item quantity="one">Erabiltzaile bakarra sor daiteke.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Erabiltzailea kendu nahi duzu?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Erabiltzailearen aplikazio eta datu guztiak ezabatuko dira."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Kendu"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ezkerreko ikonoa"</string>
<string name="right_icon" msgid="3952104823293824311">"Eskuineko ikonoa"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Eduki sakatuta eta arrastatu lauzak gehitzeko"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Eduki sakatuta eta arrastatu, lauzak berrantolatzeko"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Kentzeko, arrastatu hona"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Gutxienez sei lauza behar dituzu"</string>
<string name="qs_edit" msgid="2232596095725105230">"Editatu"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 62bb59f..5411d3d 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"خروج کاربر از سیستم"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"کاربر جدیدی اضافه میکنید؟"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"وقتی کاربر جدیدی اضافه میکنید آن فرد باید فضای خودش را تنظیم کند.\n\nهر کاربری میتواند برنامهها را برای همه کاربران دیگر بهروزرسانی کند."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"به تعداد مجاز تعداد کاربر رسیدهاید"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">میتوانید حداکثر <xliff:g id="COUNT">%d</xliff:g> کاربر اضافه کنید.</item>
+ <item quantity="other">میتوانید حداکثر <xliff:g id="COUNT">%d</xliff:g> کاربر اضافه کنید.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"کاربر حذف شود؟"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"همه برنامهها و دادههای این کاربر حذف میشود."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"حذف"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"نماد چپ"</string>
<string name="right_icon" msgid="3952104823293824311">"نماد راست"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"نگهداشتن و کشیدن برای افزودن کاشی"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"برای تغییر دادن ترتیب کاشیها، آنها را نگه دارید و بکشید"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"برای حذف، به اینجا بکشید"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"حداقل به ۶ کاشی نیاز دارید"</string>
<string name="qs_edit" msgid="2232596095725105230">"ویرایش"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 330f7d6..83345b8 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"KIRJAA KÄYTTÄJÄ ULOS"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Lisätäänkö uusi käyttäjä?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kun lisäät uuden käyttäjän, hänen tulee määrittää oman tilansa asetukset.\n\nKaikki käyttäjät voivat päivittää sovelluksia muille käyttäjille."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Käyttäjäraja saavutettu"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Voit lisätä korkeintaan <xliff:g id="COUNT">%d</xliff:g> käyttäjää.</item>
+ <item quantity="one">Käyttäjiä voi olla vain yksi.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Poistetaanko käyttäjä?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Kaikki käyttäjän tiedot ja sovellukset poistetaan."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Poista"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Vasen kuvake"</string>
<string name="right_icon" msgid="3952104823293824311">"Oikea kuvake"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Lisää osioita koskettamalla pitkään ja vetämällä"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Järjestele ruutuja koskettamalla pitkään ja vetämällä"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Poista vetämällä tähän."</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Kuusi osiota on vähimmäismäärä."</string>
<string name="qs_edit" msgid="2232596095725105230">"Muokkaa"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 47430ba..c9c252f 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"DÉCONNECTER L\'UTILISATEUR"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Ajouter un utilisateur?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Lorsque vous ajoutez un utilisateur, celui-ci doit configurer son espace.\n\nTout utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Limite d\'utilisateurs atteinte"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> utilisateur.</item>
+ <item quantity="other">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> utilisateurs.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Supprimer l\'utilisateur?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Toutes les applications et les données de cet utilisateur seront supprimées."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Supprimer"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Icône à gauche"</string>
<string name="right_icon" msgid="3952104823293824311">"Icône droite"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Maint. doigt sur écran, puis glissez-le pour aj. des tuiles"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Maint. doigt sur l\'écran, puis glissez-le pour réorg. tuiles"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Faites glisser les tuiles ici pour les supprimer"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Vous avez besoin d\'au moins six tuiles"</string>
<string name="qs_edit" msgid="2232596095725105230">"Modifier"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index c10c1d2..4559854 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -361,7 +361,7 @@
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
<string name="recents_launch_disabled_message" msgid="1624523193008871793">"L\'application <xliff:g id="APP">%s</xliff:g> est désactivée en mode sécurisé."</string>
- <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tout effacer"</string>
+ <string name="recents_stack_action_button_label" msgid="6593727103310426253">"Tout fermer"</string>
<string name="recents_drag_hint_message" msgid="2649739267073203985">"Faire glisser ici pour utiliser l\'écran partagé"</string>
<string name="recents_swipe_up_onboarding" msgid="3824607135920170001">"Balayer l\'écran vers le haut pour changer d\'application"</string>
<string name="recents_quick_scrub_onboarding" msgid="2778062804333285789">"Déplacer vers la droite pour changer rapidement d\'application"</string>
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"DÉCONNECTER L\'UTILISATEUR"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Ajouter un utilisateur ?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Lorsque vous ajoutez un utilisateur, celui-ci doit configurer son espace.\n\nN\'importe quel utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Limite nombre utilisateurs atteinte"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Vous pouvez ajouter <xliff:g id="COUNT">%d</xliff:g> profil utilisateur.</item>
+ <item quantity="other">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> profils utilisateur.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Supprimer l\'utilisateur ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Toutes les applications et les données de cet utilisateur seront supprimées."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Supprimer"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Icône gauche"</string>
<string name="right_icon" msgid="3952104823293824311">"Icône droite"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Sélectionnez et faites glisser les tuiles pour les ajouter"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Sélectionnez et faites glisser les tuiles pour réorganiser"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Faites glisser les tuiles ici pour les supprimer."</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Au minimum six tuiles sont nécessaires"</string>
<string name="qs_edit" msgid="2232596095725105230">"Modifier"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 5c6737f..9cb251e 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"PECHAR SESIÓN DO USUARIO"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Engadir un usuario novo?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Cando engadas un usuario novo, este deberá configurar o seu espazo\n\nCalquera usuario pode actualizar as aplicacións para todos os demais usuarios."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Alcanzouse o límite de usuarios"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Podes engadir ata <xliff:g id="COUNT">%d</xliff:g> usuarios.</item>
+ <item quantity="one">Só se pode crear un usuario.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Queres eliminar o usuario?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Eliminaranse todas as aplicacións e os datos deste usuario."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Eliminar"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Icona á esquerda"</string>
<string name="right_icon" msgid="3952104823293824311">"Icona á dereita"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Mantén premidas as funcións e arrástraas para engadilas"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Para reorganizar os mosaicos, mantenos premidos e arrástraos"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arrastra o elemento ata aquí para eliminalo"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Necesitas polo menos 6 mosaicos"</string>
<string name="qs_edit" msgid="2232596095725105230">"Editar"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 76e00d4..df06cbe 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"વપરાશકર્તાને લૉગઆઉટ કરો"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"નવા વપરાશકર્તાને ઉમેરીએ?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"જ્યારે તમે કોઈ નવા વપરાશકર્તાને ઉમેરો છો, ત્યારે તે વ્યક્તિને તેમનું સ્થાન સેટ કરવાની જરૂર પડે છે.\n\nકોઈપણ વપરાશકર્તા બધા અન્ય વપરાશકર્તાઓ માટે એપ્લિકેશન્સને અપડેટ કરી શકે છે."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"વપરાશકર્તા સંખ્યાની મર્યાદા"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">તમે <xliff:g id="COUNT">%d</xliff:g> વપરાશકર્તા સુધી ઉમેરી શકો છો.</item>
+ <item quantity="other">તમે <xliff:g id="COUNT">%d</xliff:g> વપરાશકર્તાઓ સુધી ઉમેરી શકો છો.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"વપરાશકર્તાને દૂર કરીએ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"આ વપરાશકર્તાની તમામ ઍપ્લિકેશનો અને ડેટા કાઢી નાખવામાં આવશે."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"દૂર કરો"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"ડાબું આઇકન"</string>
<string name="right_icon" msgid="3952104823293824311">"જમણું આઇકન"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ટાઇલ ઉમેરવા માટે તેના પર આંગળી દબાવીને ખેંચો"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ટાઇલને ફરીથી ગોઠવવા માટે આંગળી દબાવીને ખેંચો"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"દૂર કરવા માટે અહીં ખેંચો"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"તમને ઓછામાં ઓછી 6 ટાઇલની જરૂર છે"</string>
<string name="qs_edit" msgid="2232596095725105230">"સંપાદિત કરો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index ad8d505..46555a4 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"उपयोगकर्ता को प्रस्थान करवाएं"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"नया उपयोगकर्ता जोड़ें?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"जब आप कोई नया उपयोगकर्ता जोड़ते हैं तो उस व्यक्ति को अपनी जगह सेट करनी होती है.\n\nकोई भी उपयोगकर्ता बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"अब और उपयोगकर्ता नहीं जोड़े जा सकते"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">आप ज़्यादा से ज़्यादा <xliff:g id="COUNT">%d</xliff:g> उपयोगकर्ता जोड़ सकते हैं.</item>
+ <item quantity="other">आप ज़्यादा से ज़्यादा <xliff:g id="COUNT">%d</xliff:g> उपयोगकर्ता जोड़ सकते हैं.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"उपयोगकर्ता निकालें?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"इस उपयोगकर्ता के सभी ऐप और डेटा को हटा दिया जाएगा."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"निकालें"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"बायां आइकॉन"</string>
<string name="right_icon" msgid="3952104823293824311">"दायां आइकॉन"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"टाइल जोड़ने के लिए दबाएं और खींचें"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"टाइल का क्रम फिर से बदलने के लिए उन्हें दबाकर रखें और खींचें"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"हटाने के लिए यहां खींचें और छोड़ें"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"आपके पास कम से कम 6 टाइल होनी चाहिए"</string>
<string name="qs_edit" msgid="2232596095725105230">"बदलाव करें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 435fc56..7ad2fe2 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -429,9 +429,12 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ODJAVI KORISNIKA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Dodati novog korisnika?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kada dodate novog korisnika, ta osoba mora postaviti vlastiti prostor.\n\nBilo koji korisnik može ažurirati aplikacije za sve ostale korisnike."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Dosegnuto je ograničenje korisnika"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ <item quantity="few">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ <item quantity="other">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Ukloniti korisnika?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Izbrisat će se sve aplikacije i podaci ovog korisnika."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Ukloni"</string>
@@ -737,8 +740,7 @@
<string name="left_icon" msgid="3096287125959387541">"Lijeva ikona"</string>
<string name="right_icon" msgid="3952104823293824311">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Zadržite i povucite za dodavanje pločica"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Zadržite i povucite da biste preuredili pločice"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Povucite ovdje za uklanjanje"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Potrebno je barem 6 pločica"</string>
<string name="qs_edit" msgid="2232596095725105230">"Uredi"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 6574c97..f5fad27 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"FELHASZNÁLÓ KIJELENTKEZÉSE"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Új felhasználó hozzáadása?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Ha új felhasználót ad hozzá, az illetőnek be kell állítania saját tárterületét.\n\nBármely felhasználó frissítheti az alkalmazásokat valamennyi felhasználó számára."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Maximális felhasználószám elérve"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Legfeljebb <xliff:g id="COUNT">%d</xliff:g> felhasználót adhat hozzá.</item>
+ <item quantity="one">Csak egy felhasználót lehet létrehozni.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Törli a felhasználót?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"A felhasználóhoz tartozó minden adat és alkalmazás törölve lesz."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Eltávolítás"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Bal oldali ikon"</string>
<string name="right_icon" msgid="3952104823293824311">"Jobb oldali ikon"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Tartsa lenyomva, és húzza a mozaikok hozzáadásához"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Tartsa lenyomva, és húzza a mozaikok átrendezéséhez"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Húzza ide az eltávolításhoz"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Legalább hat mozaik szükséges"</string>
<string name="qs_edit" msgid="2232596095725105230">"Szerkesztés"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 20209a2..5eeca4c 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ԸՆԹԱՑԻԿ ՕԳՏՎՈՂԻ ԴՈՒՐՍ ԳՐՈՒՄ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Ավելացնե՞լ նոր պրոֆիլ:"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Երբ նոր օգտատեր եք ավելացնում, նա պետք է կարգավորի իր պրոֆիլը:\n\nՑանկացած օգտատեր կարող է թարմացնել հավելվածները մյուս բոլոր հաշիվների համար:"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Սահմանաչափը սպառված է"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Հնարավոր է ավելացնել առավելագույնը <xliff:g id="COUNT">%d</xliff:g> օգտատեր։</item>
+ <item quantity="other">Հնարավոր է ավելացնել առավելագույնը <xliff:g id="COUNT">%d</xliff:g> օգտատեր։</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Հեռացնե՞լ օգտատիրոջը:"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Այս օգտատիրոջ բոլոր հավելվածներն ու տվյալները կջնջվեն:"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Հեռացնել"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ձախ պատկերակ"</string>
<string name="right_icon" msgid="3952104823293824311">"Աջ պատկերակ"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Պահեք և քաշեք՝ սալիկներ ավելացնելու համար"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Պահեք և քաշեք՝ սալիկները վերադասավորելու համար"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Քաշեք այստեղ՝ հեռացնելու համար"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Հարկավոր է առնվազն 6 սալիկ"</string>
<string name="qs_edit" msgid="2232596095725105230">"Փոփոխել"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 5111ed6..5a61a64 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"KELUARKAN PENGGUNA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Tambahkan pengguna baru?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Saat Anda menambahkan pengguna baru, orang tersebut perlu menyiapkan ruangnya sendiri.\n\nPengguna mana pun dapat mengupdate aplikasi untuk semua pengguna lain."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Batas pengguna tercapai"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Anda dapat menambahkan hingga <xliff:g id="COUNT">%d</xliff:g> pengguna.</item>
+ <item quantity="one">Hanya dapat membuat satu pengguna.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Hapus pengguna?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Semua aplikasi dan data pengguna ini akan dihapus."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Hapus"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ikon kiri"</string>
<string name="right_icon" msgid="3952104823293824311">"Ikon kanan"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Tahan dan tarik untuk menambahkan tile"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Tahan dan tarik untuk mengatur ulang tile"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Tarik ke sini untuk menghapus"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Anda membutuhkan setidaknya 6 tile"</string>
<string name="qs_edit" msgid="2232596095725105230">"Edit"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 508b2bd..fb83ff07 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"SKRÁ NOTANDA ÚT"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Bæta nýjum notanda við?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Þegar þú bætir nýjum notanda við þarf sá notandi að setja upp svæðið sitt.\n\nHvaða notandi sem er getur uppfært forrit fyrir alla aðra notendur."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Notandahámarki náð"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Þú getur bætt við allt að <xliff:g id="COUNT">%d</xliff:g> notanda.</item>
+ <item quantity="other">Þú getur bætt við allt að <xliff:g id="COUNT">%d</xliff:g> notendum.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Fjarlægja notandann?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Öllum forritum og gögnum þessa notanda verður eytt."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Fjarlægja"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Tákn til vinstri"</string>
<string name="right_icon" msgid="3952104823293824311">"Tákn til hægri"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Haltu inni og dragðu til að bæta við reitum"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Haltu og dragðu til að endurraða flísum"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Dragðu hingað til að fjarlægja"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Reitirnir mega ekki vera færri en sex"</string>
<string name="qs_edit" msgid="2232596095725105230">"Breyta"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 467ca8d..c081eaa 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"DISCONNETTI UTENTE"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Aggiungere un nuovo utente?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Il nuovo utente, una volta aggiunto, deve impostare il proprio spazio.\n\nQualsiasi utente può aggiornare le app per tutti gli altri."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Limite di utenti raggiunto"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Puoi aggiungere fino a <xliff:g id="COUNT">%d</xliff:g> utenti.</item>
+ <item quantity="one">È possibile creare un solo utente.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Rimuovere l\'utente?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Tutte le app e i dati di questo utente verranno eliminati."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Rimuovi"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Icona sinistra"</string>
<string name="right_icon" msgid="3952104823293824311">"Icona destra"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Tieni premuto e trascina per aggiungere riquadri"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Tieni premuto e trascina per riordinare i riquadri"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Trascina qui per rimuovere"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Occorrono almeno sei riquadri"</string>
<string name="qs_edit" msgid="2232596095725105230">"Modifica"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 46d930e..d3d18ae 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -432,9 +432,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"נתק משתמש"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"האם להוסיף משתמש חדש?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"בעת הוספת משתמש חדש, על משתמש זה להגדיר את השטח שלו.\n\nכל משתמש יכול לעדכן אפליקציות עבור כל המשתמשים האחרים."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"הגעת למגבלת המשתמשים שניתן להוסיף"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="two">ניתן להוסיף עד <xliff:g id="COUNT">%d</xliff:g> משתמשים.</item>
+ <item quantity="many">ניתן להוסיף עד <xliff:g id="COUNT">%d</xliff:g> משתמשים.</item>
+ <item quantity="other">ניתן להוסיף עד <xliff:g id="COUNT">%d</xliff:g> משתמשים.</item>
+ <item quantity="one">ניתן ליצור רק משתמש אחד.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"האם להסיר את המשתמש?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"כל האפליקציות והנתונים של המשתמש הזה יימחקו."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"הסר"</string>
@@ -742,8 +746,7 @@
<string name="left_icon" msgid="3096287125959387541">"סמל שמאלי"</string>
<string name="right_icon" msgid="3952104823293824311">"סמל ימני"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"יש ללחוץ ולגרור כדי להוסיף אריחים"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"יש ללחוץ ולגרור כדי לסדר מחדש את האריחים"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"גרור לכאן כדי להסיר"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"יש צורך בשישה אריחים לכל הפחות"</string>
<string name="qs_edit" msgid="2232596095725105230">"עריכה"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index b34a5b4..8377f07 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ユーザーをログアウト"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"新しいユーザーを追加しますか?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"新しいユーザーを追加したら、そのユーザーは自分のスペースをセットアップする必要があります。\n\nすべてのユーザーは他のユーザーに代わってアプリを更新できます。"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"ユーザー数が上限に達しました"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">最大 <xliff:g id="COUNT">%d</xliff:g> 人のユーザーを追加できます。</item>
+ <item quantity="one">作成できるユーザーは 1 人のみです。</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ユーザーを削除しますか?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"このユーザーのアプリとデータがすべて削除されます。"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"削除"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"左アイコン"</string>
<string name="right_icon" msgid="3952104823293824311">"右アイコン"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"タイルを追加するには押し続けながらドラッグ"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"タイルを並べ替えるには押し続けながらドラッグ"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"削除するにはここにドラッグ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"タイルは 6 個以上必要"</string>
<string name="qs_edit" msgid="2232596095725105230">"編集"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index e490dac..fb880c7 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"მომხმარებლის გასვლა"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"დაემატოს ახალი მომხმარებელი?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"ახალი მომხმარებლის დამატებისას, ამ მომხმარებელს საკუთარი სივრცის შექმნა მოუწევს.\n\nნებისმიერ მომხმარებელს შეუძლია აპები ყველა სხვა მომხმარებლისათვის განაახლოს."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"მიღწეულია მომხმარებელთა ლიმიტი"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">შესაძლებელია <xliff:g id="COUNT">%d</xliff:g>-მდე მომხმარებლის დამატება.</item>
+ <item quantity="one">შესაძლებელია მხოლოდ ერთი მომხმარებლის შექმნა.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"გსურთ მომხმარებლის წაშლა?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ამ მომხმარებლის ყველა აპი და მონაცემი წაიშლება."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"წაშლა"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"მარცხენა ხატულა"</string>
<string name="right_icon" msgid="3952104823293824311">"მარჯვენა ხატულა"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ჩავლებით გადაიტანეთ ბლოკების დასამატებლად"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ფილების გადაწყობა შეგიძლიათ მათი ჩავლებით გადატანით"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ამოსაშლელად, ჩავლებით გადმოიტანეთ აქ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"გჭირდებათ მინიმუმ 6 ბლოკი"</string>
<string name="qs_edit" msgid="2232596095725105230">"რედაქტირება"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index eb50c28..6e372d7 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ПАЙДАЛАНУШЫНЫ ШЫҒАРУ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Жаңа пайдаланушы қосылсын ба?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Жаңа пайдаланушыны қосқанда, сол адам өз кеңістігін реттеуі керек.\n\nКез келген пайдаланушы барлық басқа пайдаланушылар үшін қолданбаларды жаңарта алады."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Пайдаланушылар саны шегіне жетті"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> пайдаланушыға дейін енгізуге болады.</item>
+ <item quantity="one">Тек бір пайдаланушыны жасауға болады.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Пайдаланушы жойылсын ба?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Осы пайдаланушының барлық қолданбалары мен деректері жойылады."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Жою"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Сол жақ белгіше"</string>
<string name="right_icon" msgid="3952104823293824311">"Оң жақ белгіше"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Қажетті элементтерді сүйреп әкеліп қойыңыз"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Бөлшектердің ретін өзгерту үшін оны басып тұрып сүйреңіз"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Керексіздерін осы жерге сүйреңіз"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Кемінде 6 бөлшек қажет"</string>
<string name="qs_edit" msgid="2232596095725105230">"Өңдеу"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 4f1d71d..eb9ed61 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ចុះឈ្មោះអ្នកប្រើចេញ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"បន្ថែមអ្នកប្រើថ្មី?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"ពេលអ្នកបន្ថែមអ្នកប្រើថ្មី អ្នកប្រើនោះត្រូវកំណត់ទំហំផ្ទាល់របស់គេ។\n\nអ្នកប្រើណាមួយក៏អាចធ្វើបច្ចុប្បន្នភាពកម្មវិធីសម្រាប់អ្នកប្រើផ្សេងបានដែរ។"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"បានឈានដល់ចំនួនកំណត់អ្នកប្រើប្រាស់"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">អ្នកអាចបញ្ចូលអ្នកប្រើប្រាស់បានរហូតដល់ <xliff:g id="COUNT">%d</xliff:g> នាក់។</item>
+ <item quantity="one">អាចបង្កើតអ្នកប្រើប្រាស់បានតែម្នាក់ប៉ុណ្ណោះ។</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"យកអ្នកប្រើចេញ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"កម្មវិធី និងទិន្នន័យទាំងអស់របស់អ្នកប្រើនេះនឹងត្រូវបានលុប។"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"យកចេញ"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"រូបតំណាងខាងឆ្វេង"</string>
<string name="right_icon" msgid="3952104823293824311">"រូបតំណាងខាងស្ដាំ"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ចុចឱ្យជាប់ រួចអូសដើម្បីបញ្ចូលប្រអប់"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ចុចឱ្យជាប់ រួចអូសដើម្បីរៀបចំប្រអប់ឡើងវិញ"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"អូសទីនេះដើម្បីយកចេញ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"អ្នកត្រូវការប្រអប់យ៉ាងតិច 6"</string>
<string name="qs_edit" msgid="2232596095725105230">"កែសម្រួល"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 49a9419b..c63dbe9 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ಬಳಕೆದಾರರನ್ನು ಲಾಗ್ಔಟ್ ಮಾಡಿ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸುವುದೇ?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"ನೀವು ಒಬ್ಬ ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿದಾಗ, ಆ ವ್ಯಕ್ತಿಯು ಅವರ ಸ್ಥಳವನ್ನು ಸ್ಥಾಪಿಸಬೇಕಾಗುತ್ತದೆ.\n\nಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗಾಗಿ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"ಬಳಕೆದಾರರ ಮಿತಿ ತಲುಪಿದೆ"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">ನೀವು <xliff:g id="COUNT">%d</xliff:g> ಬಳಕೆದಾರರವರೆಗೆ ಸೇರಿಸಬಹುದು.</item>
+ <item quantity="other">ನೀವು <xliff:g id="COUNT">%d</xliff:g> ಬಳಕೆದಾರರವರೆಗೆ ಸೇರಿಸಬಹುದು.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ಈ ಬಳಕೆದಾರರ ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುವುದು."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ತೆಗೆದುಹಾಕಿ"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"ಎಡ ಐಕಾನ್"</string>
<string name="right_icon" msgid="3952104823293824311">"ಬಲ ಐಕಾನ್"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ಟೈಲ್ಗಳನ್ನು ಸೇರಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಮತ್ತು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ಟೈಲ್ಗಳನ್ನು ಮರುಹೊಂದಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಮತ್ತು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ತೆಗೆದುಹಾಕಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"ನಿಮಗೆ ಕನಿಷ್ಠ 6 ಟೈಲ್ಗಳ ಅಗತ್ಯವಿದೆ"</string>
<string name="qs_edit" msgid="2232596095725105230">"ಎಡಿಟ್"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index ad98bce..77c983e 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"사용자 로그아웃"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"새 사용자를 추가할까요?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"추가된 새로운 사용자는 자신의 공간을 설정해야 합니다.\n\n모든 사용자는 다른 사용자들을 위하여 앱을 업데이트할 수 있습니다."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"사용자 제한 도달"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">사용자를 <xliff:g id="COUNT">%d</xliff:g>명까지 추가할 수 있습니다.</item>
+ <item quantity="one">사용자를 한 명만 만들 수 있습니다.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"사용자를 삭제할까요?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"이 사용자의 모든 앱과 데이터가 삭제됩니다."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"삭제"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"왼쪽 아이콘"</string>
<string name="right_icon" msgid="3952104823293824311">"오른쪽 아이콘"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"길게 터치하고 드래그하여 타일 추가"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"길게 터치하고 드래그하여 타일을 다시 정렬"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"여기로 드래그하여 삭제"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"6개 이상의 타일이 필요합니다."</string>
<string name="qs_edit" msgid="2232596095725105230">"수정"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 0bbdc91..e334da4 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"КОЛДОНУУЧУНУ ТУТУМДАН ЧЫГАРУУ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Жаңы колдонуучу кошосузбу?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Жаңы колдонуучу кошулганда, ал өз мейкиндигин түзүп алышы керек.\n\nКолдонмолорду бир колдонуучу жаңыртканда, ал калган бардык колдонуучулар үчүн да жаңырат."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Дагы колдонуучу кошууга болбойт"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> колдонуучуга чейин кошууга болот.</item>
+ <item quantity="one">Бир колдонуучуну гана кошууга болот.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Колдонуучу алынып салынсынбы?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Бул колдонуучунун бардык колдонмолору жана дайындары жок кылынат."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Алып салуу"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Сол жактагы сүрөтчө"</string>
<string name="right_icon" msgid="3952104823293824311">"Оң жактагы сүрөтчө"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Керектүү элементтерди сүйрөп келиңиз"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Мозаикаларды иреттөө үчүн кармап туруп, сүйрөңүз"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Алып салуу үчүн бул жерге сүйрөңүз"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Сизге жок дегенде 6 мозаика керек"</string>
<string name="qs_edit" msgid="2232596095725105230">"Түзөтүү"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 12c67db..56f9e1d 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ເອົາຜູ້ໃຊ້ອອກຈາກລະບົບ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"ເພີ່ມຜູ້ໃຊ້ໃໝ່ບໍ?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"ເມື່ອທ່ານເພີ່ມຜູ້ໃຊ້ໃໝ່, ຜູ້ໃຊ້ນັ້ນຈະຕ້ອງຕັ້ງຄ່າພື້ນທີ່ບ່ອນຈັດເກັບຂໍ້ມູນຂອງລາວ.\n\nຜູ້ໃຊ້ທຸກຄົນສາມາດອັບເດດແອັບຯຂອງຜູ້ໃຊ້ຄົນອື່ນທັງໝົດໄດ້."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"ຮອດຂີດຈຳກັດຜູ້ໃຊ້ແລ້ວ"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">ທ່ານສາມາດເພີ່ມໄດ້ສູງສຸດ <xliff:g id="COUNT">%d</xliff:g> ຄົນ.</item>
+ <item quantity="one">ສາມາດສ້າງໄດ້ໜຶ່ງຜູ້ໃຊ້ເທົ່ານັ້ນ.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ລຶບຜູ້ໃຊ້ອອກບໍ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ທຸກແອັບ ແລະ ຂໍ້ມູນຂອງຜູ້ໃຊ້ນີ້ຈະຖືກລຶບ."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ເອົາອອກ"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"ໄອຄອນຊ້າຍ"</string>
<string name="right_icon" msgid="3952104823293824311">"ໄອຄອນຂວາ"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ກົດຄ້າງໄວ້ແລ້ວລາກເພື່ອເພີ່ມຊ່ອງ"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ກົດຄ້າງໄວ້ແລ້ວລາກເພື່ອຈັດຮຽງໃໝ່"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ລາກມາບ່ອນນີ້ເພື່ອລຶບອອກ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"ທ່ານຍຕ້ອງໃຊ້ຢ່າງໜ້ອຍ 6 ຊ່ອງ"</string>
<string name="qs_edit" msgid="2232596095725105230">"ແກ້ໄຂ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index f59b1ad..b8ae38b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -432,9 +432,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ATJUNGTI NAUDOTOJĄ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Pridėti naują naudotoją?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kai pridedate naują naudotoją, šis asmuo turi nustatyti savo erdvę.\n\nBet kuris naudotojas gali atnaujinti visų kitų naudotojų programas."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Pasiekta naudotojų riba"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojo.</item>
+ <item quantity="few">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojų.</item>
+ <item quantity="many">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojo.</item>
+ <item quantity="other">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojų.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Pašalinti naudotoją?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Bus ištrinti visi šio naudotojo duomenys ir programos."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Pašalinti"</string>
@@ -742,8 +746,7 @@
<string name="left_icon" msgid="3096287125959387541">"Piktograma kairėje"</string>
<string name="right_icon" msgid="3952104823293824311">"Piktograma dešinėje"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Jei norite pridėti išklotinių, laikykite nuspaudę ir vilkite"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Norėdami pertvarkyti išklot., laikykite nuspaudę ir vilkite"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Vilkite čia, jei norite pašalinti"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Turi būti bent 6 išklotinės"</string>
<string name="qs_edit" msgid="2232596095725105230">"Redaguoti"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 0075094..4a0ee10 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -429,9 +429,12 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ATTEIKT LIETOTĀJU"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Vai pievienot jaunu lietotāju?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kad pievienosiet jaunu lietotāju, viņam būs jāizveido savs profils.\n\nIkviens lietotājs var atjaunināt lietotnes citu lietotāju vietā."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Sasniegts lietotāju ierobežojums"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="zero">Varat pievienot ne vairāk kā <xliff:g id="COUNT">%d</xliff:g> lietotājus.</item>
+ <item quantity="one">Varat pievienot ne vairāk kā <xliff:g id="COUNT">%d</xliff:g> lietotāju.</item>
+ <item quantity="other">Varat pievienot ne vairāk kā <xliff:g id="COUNT">%d</xliff:g> lietotājus.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Vai noņemt lietotāju?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Tiks dzēstas visas šī lietotāja lietotnes un dati."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Noņemt"</string>
@@ -737,8 +740,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ikona kreisajā pusē"</string>
<string name="right_icon" msgid="3952104823293824311">"Ikona labajā pusē"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Lai pievienotu elementus, pieturiet tos un velciet"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Lai pārkārtotu elementus, turiet un velciet tos"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Lai noņemtu vienumus, velciet tos šeit."</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Nepieciešami vismaz 6 elementi"</string>
<string name="qs_edit" msgid="2232596095725105230">"Rediģēt"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 68dc653..bf9cc35 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ОДЈАВИ ГО КОРИСНИКОТ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Да се додаде нов корисник?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Кога додавате нов корисник, тоа лице треба да го постави својот простор.\n\nСекој корисник може да ажурира апликации за сите други корисници."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Достигнато ограничување на корисник"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Може да додадете најмногу <xliff:g id="COUNT">%d</xliff:g> корисник.</item>
+ <item quantity="other">Може да додадете најмногу <xliff:g id="COUNT">%d</xliff:g> корисници.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Да се отстрани корисникот?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Сите апликации и податоци од овој корисник ќе се избришат."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Отстрани"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Лева икона"</string>
<string name="right_icon" msgid="3952104823293824311">"Десна икона"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Задржете и влечете за да додадете плочки"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Задржете и влечете за да ги преуредите плочките"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Повлечете тука за да се отстрани"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Потребни ви се најмалку 6 плочки"</string>
<string name="qs_edit" msgid="2232596095725105230">"Измени"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 507cbbf..fefd073 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ഉപയോക്താവിനെ ലോഗൗട്ട് ചെയ്യുക"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"പുതിയ ഉപയോക്താവിനെ ചേർക്കണോ?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"നിങ്ങൾ ഒരു പുതിയ ഉപയോക്താവിനെ ചേർക്കുമ്പോൾ, ആ വ്യക്തിക്ക് അവരുടെ ഇടം സജ്ജീകരിക്കേണ്ടതുണ്ട്.\n\nമറ്റ് എല്ലാ ഉപയോക്താക്കൾക്കുമായി ഏതൊരു ഉപയോക്താവിനും അപ്ലിക്കേഷനുകൾ അപ്ഡേറ്റ് ചെയ്യാനാവും."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"ഉപയോക്തൃ പരിധി എത്തി"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">നിങ്ങൾക്ക് <xliff:g id="COUNT">%d</xliff:g> ഉപയോക്താക്കളെ വരെ ചേർക്കാനാവും.</item>
+ <item quantity="one">ഒരു ഉപയോക്താവിന് മാത്രമേ സൃഷ്ടിക്കാനാവൂ.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ഉപയോക്താവിനെ ഇല്ലാതാക്കണോ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ഈ ഉപയോക്താവിന്റെ എല്ലാ ആപ്സും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"നീക്കംചെയ്യുക"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"ഇടതുവശത്തെ ചിഹ്നം"</string>
<string name="right_icon" msgid="3952104823293824311">"വലതുവശത്തെ ചിഹ്നം"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ടൈലുകൾ ചേർക്കാൻ ക്ലിക്ക് ചെയ്ത് ഇഴയ്ക്കുക"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ടൈലുകൾ പുനഃക്രമീകരിക്കാൻ അമർത്തിപ്പിടിച്ച് വലിച്ചിടുക"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"നീക്കംചെയ്യുന്നതിന് ഇവിടെ വലിച്ചിടുക"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"നിങ്ങൾക്ക് ചുരുങ്ങിയത് 6 ടൈലുകൾ വേണം"</string>
<string name="qs_edit" msgid="2232596095725105230">"എഡിറ്റുചെയ്യുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 8e27efe..e86dc64 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -424,9 +424,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ХЭРЭГЛЭГЧЭЭС ГАРАХ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Шинэ хэрэглэгч нэмэх үү?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Та шинэ хэрэглэгч нэмбэл тухайн хүн өөрийн профайлыг тохируулах шаардлагатай.\n\nАль ч хэрэглэгч бүх хэрэглэгчийн апп-уудыг шинэчлэх боломжтой."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Хэрэглэгчийн хязгаарт хүрсэн"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Та <xliff:g id="COUNT">%d</xliff:g> хүртэлх хэрэглэгч нэмэх боломжтой.</item>
+ <item quantity="one">Зөвхөн нэг хэрэглэгч үүсгэх боломжтой.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Хэрэглэгчийг устгах уу?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Энэ хэрэглэгчийн бүх апп болон мэдээлэл устах болно."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Арилгах"</string>
@@ -730,8 +732,7 @@
<string name="left_icon" msgid="3096287125959387541">"Зүүн дүрс тэмдэг"</string>
<string name="right_icon" msgid="3952104823293824311">"Баруун дүрс тэмдэг"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Хавтанд нэмэхийн тулд дараад чирэх"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Хавтангуудыг дахин засварлахын тулд дараад чирнэ үү"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Устгахын тулд энд зөөнө үү"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Танд хамгийн багадаа 6 хавтан шаардлагатай"</string>
<string name="qs_edit" msgid="2232596095725105230">"Засах"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 1c16dc2..a9f9af4 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"वापरकर्त्यास लॉगआउट करा"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"नवीन वापरकर्ता जोडायचा?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"तुम्ही एक नवीन वापरकर्ता जोडता तेव्हा, त्या व्यक्तीने त्यांचे स्थान सेट करणे आवश्यक असते.\n\nकोणताही वापरकर्ता इतर सर्व वापरकर्त्यांसाठी अॅप्स अपडेट करू शकतो."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"वापरकर्ता मर्यादा गाठली"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">तुम्ही <xliff:g id="COUNT">%d</xliff:g> वापरकर्त्यापर्यंत जोडू शकता.</item>
+ <item quantity="other">तुम्ही <xliff:g id="COUNT">%d</xliff:g> वापरकर्त्यांपर्यंत जोडू शकता.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"वापरकर्त्यास काढायचे?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"या वापरकर्त्याचे सर्व अॅप्स आणि डेटा काढून टाकला जाईल."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"काढा"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"डावे आयकन"</string>
<string name="right_icon" msgid="3952104823293824311">"उजवे आयकन"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"टाइल जोडण्यासाठी धरून ठेवा आणि ड्रॅग करा"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"टाइलची पुनर्रचना करण्यासाठी धरून ठेवा आणि ड्रॅग करा"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"काढण्यासाठी येथे ड्रॅग करा"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"तुम्हाला किमान ६ टाइलची गरज आहे"</string>
<string name="qs_edit" msgid="2232596095725105230">"संपादित करा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 947c0ff..6ec7889 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"LOG KELUAR PENGGUNA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Tambah pengguna baharu?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Apabila anda menambah pengguna baharu, orang itu perlu menyediakan ruang mereka.\n\nMana-mana pengguna boleh mengemas kini apl untuk semua pengguna lain."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Had pengguna dicapai"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Anda boleh menambah sehingga <xliff:g id="COUNT">%d</xliff:g> pengguna.</item>
+ <item quantity="one">Hanya satu pengguna yang boleh dibuat.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Alih keluar pengguna?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Semua apl dan data pengguna ini akan dipadamkan."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Alih keluar"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ikon kiri"</string>
<string name="right_icon" msgid="3952104823293824311">"Ikon kanan"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Tahan dan seret untuk menambah jubin"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Tahan dan seret untuk mengatur semula jubin"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Seret ke sini untuk mengalih keluar"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Anda memerlukan sekurang-kurangnya 6 jubin"</string>
<string name="qs_edit" msgid="2232596095725105230">"Edit"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 8264240..7afb3b9 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"အသုံးပြုသူ ထွက်လိုက်ပါ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"အသုံးပြုသူအသစ်ကို ထည့်မလား။"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"သင်ထည့်လိုက်သော အသုံးပြုသူအသစ်သည် ၎င်း၏နေရာကို သတ်မှတ်စီစဉ်ရန် လိုအပ်သည်။\n\nမည်သည့်အသုံးပြုသူမဆို ကျန်သူများအားလုံးအတွက် အက်ပ်များကို အပ်ဒိတ်လုပ်ပေးနိုင်သည်။"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"အသုံးပြုသူ အကန့်အသတ် ပြည့်သွားပါပြီ"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">အသုံးပြုသူ <xliff:g id="COUNT">%d</xliff:g> ဦးအထိ ထည့်နိုင်သည်။</item>
+ <item quantity="one">အသုံးပြုသူ တစ်ဦးသာ ထည့်နိုင်သည်။</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"သုံးစွဲသူကိုဖယ်ရှားမည်လား?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ဤအသုံးပြုသူ၏ ဒေတာနှင့် အပ်ဖ်များအားလုံး ဖျက်လိုက်ပါမည်"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ဖယ်ရှားရန်"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"လက်ဝဲသင်္ကေတ"</string>
<string name="right_icon" msgid="3952104823293824311">"လက်ယာသင်္ကေတ"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"အကွက်များထည့်ရန် ဖိဆွဲပါ"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"အကွက်ငယ်များ ပြန်စီစဉ်ရန် ဖိပြီးဆွဲပါ"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ဖယ်ရှားရန် ဤနေရာသို့ဖိဆွဲပါ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"အနည်းဆုံး ၆ ကွက် ရှိရမည်"</string>
<string name="qs_edit" msgid="2232596095725105230">"တည်းဖြတ်ပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 8c8e905..b817877 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"LOGG UT BRUKER"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Vil du legge til en ny bruker?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Når du legger til en ny bruker, må vedkommende konfigurere sitt eget område.\n\nAlle brukere kan oppdatere apper for alle andre brukere."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Grensen for antall brukere er nådd"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Du kan legge til opptil <xliff:g id="COUNT">%d</xliff:g> brukere.</item>
+ <item quantity="one">Du kan bare opprette én bruker.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Vil du fjerne brukeren?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Alle apper og data som tilhører denne brukeren, blir slettet."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Fjern"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Venstre-ikon"</string>
<string name="right_icon" msgid="3952104823293824311">"Høyre-ikon"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Hold og dra for å legge til ruter"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Hold og dra for å endre rekkefølge for ruter"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Dra hit for å fjerne"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Du trenger minst seks ruter"</string>
<string name="qs_edit" msgid="2232596095725105230">"Endre"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 1b8149e..6cc405f 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"प्रयोगकर्ता लगआउट गर्नुहोस्"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"नयाँ प्रयोगकर्ता थप्ने हो?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"जब तपाईँले नयाँ प्रयोगकर्ता थप्नुहुन्छ, त्यस प्रयोगकर्ताले आफ्नो स्थान स्थापना गर्न पर्ने छ।\n\nकुनै पनि प्रयोगकर्ताले सबै अन्य प्रयोगकर्ताहरूका लागि अनुप्रयोगहरू अद्यावधिक गर्न सक्छन्।"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"प्रयोगकर्ताको सीमा पुग्यो"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">तपाईं अधिकतम <xliff:g id="COUNT">%d</xliff:g> प्रयोगहरू मात्र थप्न सक्नुहुन्छ।</item>
+ <item quantity="one">एउटा प्रयोगकर्ता मात्र सिर्जना गर्न सकिन्छ।</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"प्रयोगकर्ता हटाउन चाहनुहुन्छ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"यस प्रयोगकर्ताको सबै अनुप्रयोगहरू तथा डेटा हटाइनेछ।"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"हटाउनुहोस्"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"बायाँतिर देखाउने आइकन"</string>
<string name="right_icon" msgid="3952104823293824311">"दायाँतिरको आइकन"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"टाइलहरू थप्न होल्ड गरी ड्र्याग गर्नुहोस्"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"टाइलहरू पुनः क्रमबद्ध गर्न होल्ड गरी ड्र्याग गर्नुहोस्"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"हटाउनका लागि यहाँ तान्नुहोस्"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"तपाईंलाई कम्तीमा ६ वटा टाइलहरू चाहिन्छ"</string>
<string name="qs_edit" msgid="2232596095725105230">"सम्पादन गर्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 61dba4a..e8c6f8d 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"GEBRUIKER UITLOGGEN"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Nieuwe gebruiker toevoegen?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Wanneer u een nieuwe gebruiker toevoegt, moet die persoon zijn eigen profiel instellen.\n\nElke gebruiker kan apps updaten voor alle andere gebruikers."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Gebruikerslimiet bereikt"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Je kunt maximaal <xliff:g id="COUNT">%d</xliff:g> gebruikers toevoegen.</item>
+ <item quantity="one">Er kan maar één gebruiker worden gemaakt.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Gebruiker verwijderen?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Alle apps en gegevens van deze gebruiker worden verwijderd."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Verwijderen"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Pictogram links"</string>
<string name="right_icon" msgid="3952104823293824311">"Pictogram rechts"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Houd vast en sleep om tegels toe te voegen"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Houd vast en sleep om tegels opnieuw in te delen"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Sleep hier naartoe om te verwijderen"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Je hebt minimaal zes tegels nodig"</string>
<string name="qs_edit" msgid="2232596095725105230">"Bewerken"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 7f79090..277181f 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ୟୁଜରଙ୍କୁ ଲଗଆଉଟ୍ କରନ୍ତୁ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"ନୂତନ ୟୁଜର୍ ଯୋଡ଼ିବେ?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"ଜଣେ ନୂଆ ୟୁଜର୍ଙ୍କୁ ଯୋଡ଼ିବାବେଳେ, ସେହି ବ୍ୟକ୍ତିଙ୍କୁ ସ୍ଥାନ ସେଟ୍ କରିବାକୁ ପଡ଼ିବ। \n \n ଅନ୍ୟ ସମସ୍ତ ୟୁଜର୍ଙ୍କ ପାଇଁ ଯେକୌଣସି ୟୁଜର୍ ଆପ୍ଗୁଡ଼ିକୁ ଅପଡେଟ୍ କରିପାରିବେ।"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"ଉପଯୋଗକର୍ତ୍ତା ସୀମାରେ ପହଞ୍ଚିଛି"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">କେବଳ <xliff:g id="COUNT">%d</xliff:g> ଉପଯୋଗକର୍ତ୍ତା ହିଁ ତିଆରି କରିହେବ।</item>
+ <item quantity="one">କେବଳ ଜଣେ ଉପଯୋଗକର୍ତ୍ତା ହିଁ ତିଆରି କରିହେବ।</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ୟୁଜରଙ୍କୁ ବାହାର କରିବେ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ଏହି ୟୁଜରଙ୍କ ସମସ୍ତ ଆପ୍ ଓ ଡାଟା ଡିଲିଟ୍ ହେବ।"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ବାହାର କରନ୍ତୁ"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"ବାମ ଆଇକନ୍"</string>
<string name="right_icon" msgid="3952104823293824311">"ଡାହାଣ ଆଇକନ୍"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ଟାଇଲ୍ ଯୋଡ଼ିବା ପାଇଁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ଟାଇଲ୍ ପୁଣି ଆୟୋଜିତ କରିବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"ଆପଣଙ୍କର ଅତିକମରେ 6ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string>
<string name="qs_edit" msgid="2232596095725105230">"ଏଡିଟ୍ କରନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index c6ec96e..6509871 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ਉਪਭੋਗਤਾ ਨੂੰ ਲੌਗ ਆਉਟ ਕਰੋ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"ਕੀ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਨਾ ਹੈ?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"ਜਦੋਂ ਤੁਸੀਂ ਇੱਕ ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਦੇ ਹੋ, ਉਸ ਵਿਅਕਤੀ ਨੂੰ ਆਪਣੀ ਜਗ੍ਹਾ ਸਥਾਪਤ ਕਰਨ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ।\n\nਕੋਈ ਵੀ ਵਰਤੋਂਕਾਰ ਹੋਰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਸਕਦਾ ਹੈ।"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਸੀਮਾ ਪੂਰੀ ਹੋਈ"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">ਤੁਸੀਂ <xliff:g id="COUNT">%d</xliff:g> ਤੱਕ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ।</item>
+ <item quantity="other">ਤੁਸੀਂ <xliff:g id="COUNT">%d</xliff:g> ਤੱਕ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ।</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"ਕੀ ਵਰਤੋਂਕਾਰ ਹਟਾਉਣਾ ਹੈ?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ਇਸ ਉਪਭੋਗਤਾ ਦੇ ਸਾਰੇ ਐਪਸ ਅਤੇ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ਹਟਾਓ"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"ਖੱਬਾ ਪ੍ਰਤੀਕ"</string>
<string name="right_icon" msgid="3952104823293824311">"ਸੱਜਾ ਪ੍ਰਤੀਕ"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ਟਾਇਲਾਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਫੜ੍ਹ ਕੇ ਰੱਖੋ ਅਤੇ ਘਸੀਟੋ"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ਟਾਇਲਾਂ ਨੂੰ ਮੁੜ-ਵਿਵਸਥਿਤ ਕਰਨ ਲਈ ਫੜ੍ਹ ਕੇ ਘਸੀਟੋ"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ਹਟਾਉਣ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"ਤੁਹਾਨੂੰ ਘੱਟੋ-ਘੱਟ 6 ਟਾਇਲਾਂ ਦੀ ਲੋੜ ਪਵੇਗੀ"</string>
<string name="qs_edit" msgid="2232596095725105230">"ਸੰਪਾਦਨ ਕਰੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 33a8117..2959a8d 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -432,9 +432,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"WYLOGUJ UŻYTKOWNIKA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Dodać nowego użytkownika?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Gdy dodasz nowego użytkownika, musi on skonfigurować swój profil.\n\nKażdy użytkownik może aktualizować aplikacje wszystkich innych użytkowników."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Osiągnięto limit użytkowników"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="few">Możesz dodać maksymalnie <xliff:g id="COUNT">%d</xliff:g> użytkowników.</item>
+ <item quantity="many">Możesz dodać maksymalnie <xliff:g id="COUNT">%d</xliff:g> użytkowników.</item>
+ <item quantity="other">Możesz dodać maksymalnie <xliff:g id="COUNT">%d</xliff:g> użytkownika.</item>
+ <item quantity="one">Można utworzyć tylko jednego użytkownika.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Usunąć użytkownika?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Wszystkie aplikacje i dane tego użytkownika zostaną usunięte."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Usuń"</string>
@@ -742,8 +746,7 @@
<string name="left_icon" msgid="3096287125959387541">"Lewa ikona"</string>
<string name="right_icon" msgid="3952104823293824311">"Prawa ikona"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Przytrzymaj i przeciągnij, by dodać kafelki"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Przytrzymaj i przeciągnij, by przestawić kafelki"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Przeciągnij tutaj, by usunąć"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Potrzebnych jest co najmniej sześć kafelków"</string>
<string name="qs_edit" msgid="2232596095725105230">"Edytuj"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 60825b7..ba50c13 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -160,7 +160,7 @@
<string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
<string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
- <string name="accessibility_no_sim" msgid="8274017118472455155">"Sem SIM."</string>
+ <string name="accessibility_no_sim" msgid="8274017118472455155">"Sem chip."</string>
<string name="accessibility_cell_data" msgid="5326139158682385073">"Dados móveis"</string>
<string name="accessibility_cell_data_on" msgid="5927098403452994422">"Dados móveis ativados"</string>
<string name="cell_data_off_content_description" msgid="4356113230238585072">"Dados móveis desativados"</string>
@@ -168,7 +168,7 @@
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Tethering Bluetooth."</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avião."</string>
<string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN ativada."</string>
- <string name="accessibility_no_sims" msgid="3957997018324995781">"Sem cartão SIM."</string>
+ <string name="accessibility_no_sims" msgid="3957997018324995781">"Sem chip."</string>
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Alteração de rede da operadora"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Abrir detalhes da bateria"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Bateria em <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"DESCONECTAR USUÁRIO"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Adicionar novo usuário?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Quando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para os demais usuários."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Limite de usuários atingido"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuário.</item>
+ <item quantity="other">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuários.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Remover usuário?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Todos os apps e dados deste usuário serão excluídos."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Remover"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ícone à esquerda"</string>
<string name="right_icon" msgid="3952104823293824311">"Ícone à direita"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Mantenha pressionado e arraste para adicionar blocos"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Mantenha pressionado e arraste para reorganizar os blocos"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arraste aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"É preciso haver pelo menos seis blocos"</string>
<string name="qs_edit" msgid="2232596095725105230">"Editar"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 4be7281..a03dba2 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"TERMINAR SESSÃO DO UTILIZADOR"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Adicionar um novo utilizador?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Ao adicionar um novo utilizador, essa pessoa tem de configurar o respetivo espaço.\n\nQualquer utilizador pode atualizar aplicações para todos os outros utilizadores."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Limite de utilizadores alcançado"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Pode adicionar até <xliff:g id="COUNT">%d</xliff:g> utilizadores.</item>
+ <item quantity="one">Apenas é possível criar um utilizador.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Pretende remover o utilizador?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Serão eliminados todos os dados e todas as aplicações deste utilizador."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Remover"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ícone esquerdo"</string>
<string name="right_icon" msgid="3952104823293824311">"Ícone direito"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Toque sem soltar e arraste para adicionar mosaicos."</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Tocar sem soltar e arrastar para reorganizar os mosaicos"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arrastar para aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Necessita de, pelo menos, 6 mosaicos."</string>
<string name="qs_edit" msgid="2232596095725105230">"Editar"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 60825b7..ba50c13 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -160,7 +160,7 @@
<string name="data_connection_roaming" msgid="6037232010953697354">"Roaming"</string>
<string name="data_connection_edge" msgid="871835227939216682">"EDGE"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"Wi-Fi"</string>
- <string name="accessibility_no_sim" msgid="8274017118472455155">"Sem SIM."</string>
+ <string name="accessibility_no_sim" msgid="8274017118472455155">"Sem chip."</string>
<string name="accessibility_cell_data" msgid="5326139158682385073">"Dados móveis"</string>
<string name="accessibility_cell_data_on" msgid="5927098403452994422">"Dados móveis ativados"</string>
<string name="cell_data_off_content_description" msgid="4356113230238585072">"Dados móveis desativados"</string>
@@ -168,7 +168,7 @@
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Tethering Bluetooth."</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avião."</string>
<string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN ativada."</string>
- <string name="accessibility_no_sims" msgid="3957997018324995781">"Sem cartão SIM."</string>
+ <string name="accessibility_no_sims" msgid="3957997018324995781">"Sem chip."</string>
<string name="carrier_network_change_mode" msgid="8149202439957837762">"Alteração de rede da operadora"</string>
<string name="accessibility_battery_details" msgid="7645516654955025422">"Abrir detalhes da bateria"</string>
<string name="accessibility_battery_level" msgid="7451474187113371965">"Bateria em <xliff:g id="NUMBER">%d</xliff:g> por cento."</string>
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"DESCONECTAR USUÁRIO"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Adicionar novo usuário?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Quando você adiciona um novo usuário, essa pessoa precisa configurar o próprio espaço.\n\nQualquer usuário pode atualizar apps para os demais usuários."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Limite de usuários atingido"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuário.</item>
+ <item quantity="other">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuários.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Remover usuário?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Todos os apps e dados deste usuário serão excluídos."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Remover"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ícone à esquerda"</string>
<string name="right_icon" msgid="3952104823293824311">"Ícone à direita"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Mantenha pressionado e arraste para adicionar blocos"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Mantenha pressionado e arraste para reorganizar os blocos"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arraste aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"É preciso haver pelo menos seis blocos"</string>
<string name="qs_edit" msgid="2232596095725105230">"Editar"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 618c890..149803b 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -431,9 +431,12 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"DECONECTAȚI UTILIZATORUL"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Adăugați un utilizator nou?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Când adăugați un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOrice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Ați atins limita de utilizatori"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="few">Puteți adăuga maximum <xliff:g id="COUNT">%d</xliff:g> utilizatori.</item>
+ <item quantity="other">Puteți adăuga maximum <xliff:g id="COUNT">%d</xliff:g> de utilizatori.</item>
+ <item quantity="one">Poate fi creat doar un utilizator.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Eliminați utilizatorul?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Toate aplicațiile și datele acestui utilizator vor fi șterse."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Eliminați"</string>
@@ -739,8 +742,7 @@
<string name="left_icon" msgid="3096287125959387541">"Pictograma din stânga"</string>
<string name="right_icon" msgid="3952104823293824311">"Pictograma din dreapta"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Țineți apăsat și trageți pentru a adăuga piese"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Țineți apăsat și trageți pentru a rearanja piesele"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Trageți aici pentru a elimina"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Aveți nevoie de cel puțin 6 piese"</string>
<string name="qs_edit" msgid="2232596095725105230">"Editați"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c754c36..7a756cd 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -434,9 +434,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ВЫЙТИ ОТ ИМЕНИ ПОЛЬЗОВАТЕЛЯ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Добавить пользователя?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Когда вы добавите пользователя, ему потребуется настроить профиль.\n\nЛюбой пользователь устройства может обновлять приложения для всех аккаунтов."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Достигнут лимит"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователя.</item>
+ <item quantity="few">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователей.</item>
+ <item quantity="many">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователей.</item>
+ <item quantity="other">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователя.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Удалить аккаунт?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Все приложения и данные этого пользователя будут удалены."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Удалить"</string>
@@ -744,8 +748,7 @@
<string name="left_icon" msgid="3096287125959387541">"Значок \"Влево\""</string>
<string name="right_icon" msgid="3952104823293824311">"Значок \"Вправо\""</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Перетащите нужные элементы"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Чтобы изменить порядок элементов, перетащите их"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Чтобы удалить, перетащите сюда"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Должно остаться не менее 6 элементов"</string>
<string name="qs_edit" msgid="2232596095725105230">"Изменить"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 8ec763d..e29aeda 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"පරිශීලකයා වරන්න"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"අලුත් පරිශීලකයෙක් එක් කරන්නද?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"ඔබ අලුත් පරිශීලකයෙක් එකතු කරන විට, එම පුද්ගලයා ඔහුගේ වැඩ කරන ඉඩ සකසා ගත යුතුය.\n\nසියළුම අනෙක් පරිශීලකයින් සඳහා ඕනෑම පරිශීලකයෙකුට යාවත්කාලීන කළ හැක."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"පරිශීලක සීමාවට ළඟා විය"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">ඔබට පරිශීලකයින් <xliff:g id="COUNT">%d</xliff:g>ක් දක්වා එක් කළ හැකිය.</item>
+ <item quantity="other">ඔබට පරිශීලකයින් <xliff:g id="COUNT">%d</xliff:g>ක් දක්වා එක් කළ හැකිය.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"පරිශීලකයා ඉවත් කරන්නද?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"මෙම පරිශීලකයාගේ සියලු යෙදුම් සහ දත්ත මකනු ඇත."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ඉවත් කරන්න"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"වම් නිරූපකය"</string>
<string name="right_icon" msgid="3952104823293824311">"දකුණු නිරූපකය"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ටයිල් එක් කිරීමට අල්ලාගෙන සිට අදින්න"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ටයිල් නැවත සකස් කිරීමට අල්ලාගෙන සිට අදින්න"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ඉවත් කිරීමට මෙතැනට අදින්න"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"ඔබ අවම වශයෙන් ටයිල් 6ක් අවශ්ය වේ"</string>
<string name="qs_edit" msgid="2232596095725105230">"සංස්කරණය"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index de32ae5..8193e3f 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -434,9 +434,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ODHLÁSIŤ POUŽÍVATEĽA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Pridať nového používateľa?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Keď pridáte nového používateľa, musí si nastaviť vlastný priestor.\n\nKtorýkoľvek používateľ môže aktualizovať aplikácie všetkých ostatných používateľov."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Dosiahnutý limit počtu používateľov"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="few">Môžete pridať maximálne <xliff:g id="COUNT">%d</xliff:g> používateľov.</item>
+ <item quantity="many">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item>
+ <item quantity="other">Môžete pridať maximálne <xliff:g id="COUNT">%d</xliff:g> používateľov.</item>
+ <item quantity="one">Môžete vytvoriť iba jedného používateľa.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Odstrániť používateľa?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Všetky aplikácie a údaje tohto používateľa budú odstránené."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Odstrániť"</string>
@@ -744,8 +748,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ľavá ikona"</string>
<string name="right_icon" msgid="3952104823293824311">"Pravá ikona"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Pridržaním a presunutím pridáte dlaždice"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Dlaždice môžete usporiadať pridržaním a presunutím"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Presunutím sem odstránite"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Potrebujete aspoň šesť dlaždíc"</string>
<string name="qs_edit" msgid="2232596095725105230">"Upraviť"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 11de2d5..71694417 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -434,9 +434,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ODJAVA UPORABNIKA"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Dodajanje novega uporabnika?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Ko dodate novega uporabnika, mora ta nastaviti svoj prostor.\n\nVsak uporabnik lahko posodobi aplikacije za vse druge uporabnike."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Omejitev uporabnikov je dosežena"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnika.</item>
+ <item quantity="two">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnika.</item>
+ <item quantity="few">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnike.</item>
+ <item quantity="other">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnikov.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Želite odstraniti uporabnika?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Vse aplikacije in podatki tega uporabnika bodo izbrisani."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Odstrani"</string>
@@ -744,8 +748,7 @@
<string name="left_icon" msgid="3096287125959387541">"Leva ikona"</string>
<string name="right_icon" msgid="3952104823293824311">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Držite in povlecite, da dodate ploščice"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Držite in povlecite, da prerazporedite ploščice"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Če želite odstraniti, povlecite sem"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Imeti morate vsaj 6 ploščic"</string>
<string name="qs_edit" msgid="2232596095725105230">"Uredi"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index ab7076c..f2903ef 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"NXJERRJA E PËRDORUESIT NGA IDENTIFIKIMI"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Të shtohet përdorues i ri?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kur shton një përdorues të ri, ai person duhet të konfigurojë hapësirën e vet.\n\nÇdo përdorues mund t\'i përditësojë aplikacionet për të gjithë përdoruesit e tjerë."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"U arrit kufiri i përdoruesve"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Mund të shtosh deri në <xliff:g id="COUNT">%d</xliff:g> përdorues.</item>
+ <item quantity="one">Mund të krijohet vetëm një përdorues.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Të hiqet ky përdorues?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Të gjitha aplikacionet dhe të dhënat e këtij përdoruesi do të fshihen."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Hiqe"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Ikona majtas"</string>
<string name="right_icon" msgid="3952104823293824311">"Ikona djathtas"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Mbaje të shtypur dhe zvarrit për të shtuar pllakëza"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Mbaje të shtypur dhe zvarrit për të risistemuar pllakëzat"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Zvarrit këtu për ta hequr"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Të duhen të paktën 6 pllakëza"</string>
<string name="qs_edit" msgid="2232596095725105230">"Redakto"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 2dbdc3d..e77b080 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -429,9 +429,12 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ОДЈАВИ КОРИСНИКА"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Додајете новог корисника?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Када додате новог корисника, та особа треба да подеси свој простор.\n\nСваки корисник може да ажурира апликације за све остале кориснике."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Достигнут максимални број корисника"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Можете да додате највише <xliff:g id="COUNT">%d</xliff:g> корисника.</item>
+ <item quantity="few">Можете да додате највише <xliff:g id="COUNT">%d</xliff:g> корисника.</item>
+ <item quantity="other">Можете да додате највише <xliff:g id="COUNT">%d</xliff:g> корисника.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Желите ли да уклоните корисника?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Све апликације и подаци овог корисника ће бити избрисани."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Уклони"</string>
@@ -737,8 +740,7 @@
<string name="left_icon" msgid="3096287125959387541">"Лева икона"</string>
<string name="right_icon" msgid="3952104823293824311">"Десна икона"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Задржите и превуците да бисте додали плочице"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Задржите и превуците да бисте променили распоред плочица"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Превуците овде да бисте уклонили"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Треба да изаберете најмање 6 плочица"</string>
<string name="qs_edit" msgid="2232596095725105230">"Измени"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 4b05e27..ccc498e 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"LOGGA UT ANVÄNDAREN"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Lägga till ny användare?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"När du lägger till en ny användare måste den personen konfigurera sitt utrymme.\n\nAlla användare kan uppdatera appar för samtliga användares räkning."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Användargränsen har nåtts"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Det går att lägga till upp till <xliff:g id="COUNT">%d</xliff:g> användare.</item>
+ <item quantity="one">Det går bara att skapa en användare.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Vill du ta bort användaren?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Alla appar och all data som tillhör den här användaren raderas."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Ta bort"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Vänster ikon"</string>
<string name="right_icon" msgid="3952104823293824311">"Höger ikon"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Lägg till rutor genom att trycka och dra"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Ordna om rutor genom att trycka och dra"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Ta bort genom att dra här"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Minst sex rutor måste finnas kvar"</string>
<string name="qs_edit" msgid="2232596095725105230">"Redigera"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 7cb38b2..83e9997 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ONDOA MTUMIAJI"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Ungependa kuongeza mtumiaji?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Mtumiaji mpya utakayemwongeza atahitaji kuongeza akaunti yake.\n\nMtumiaji yoyote anaweza kusasisha programu kwa niaba ya wengine wote."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Umefikia kima cha juu cha watumiaji"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Unaruhusiwa kuongeza hadi watumiaji <xliff:g id="COUNT">%d</xliff:g>.</item>
+ <item quantity="one">Unaruhusiwa kuongeza mtumiaji mmoja pekee.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Je, ungependa kuondoa mtumiaji?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Programu na data yote ya mtumiaji huyu itafutwa."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Ondoa"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Aikoni ya kushoto"</string>
<string name="right_icon" msgid="3952104823293824311">"Aikoni ya kulia"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Shikilia na uburute ili uongeze vigae"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Shikilia na uburute ili upange vigae upya"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Buruta hapa ili uondoe"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Unahitaji angalau vigae 6"</string>
<string name="qs_edit" msgid="2232596095725105230">"Badilisha"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 84041a9..6f06ab5 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"பயனரை வெளியேற்று"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"புதியவரைச் சேர்க்கவா?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"புதிய பயனரைச் சேர்க்கும்போது, அவர் தனக்கான இடத்தை அமைக்க வேண்டும்.\n\nஎந்தவொரு பயனரும், மற்ற எல்லா பயனர்களுக்காகவும் பயன்பாடுகளைப் புதுப்பிக்கலாம்."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"பயனர் வரம்பை அடைந்துவிட்டீர்கள்"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> பயனர்கள் வரை சேர்க்க முடியும்.</item>
+ <item quantity="one">ஒரு பயனரை மட்டுமே சேர்க்க முடியும்.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"பயனரை அகற்றவா?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"இந்தப் பயனரின் எல்லா பயன்பாடுகளும் தரவும் நீக்கப்படும்."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"அகற்று"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"இடப்புற ஐகான்"</string>
<string name="right_icon" msgid="3952104823293824311">"வலப்புற ஐகான்"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"கட்டங்களைச் சேர்க்க, அவற்றைப் பிடித்து இழுக்கவும்"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"கட்டங்களை மறுவரிசைப்படுத்த அவற்றைப் பிடித்து இழுக்கவும்"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"அகற்ற, இங்கே இழுக்கவும்"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"குறைந்தது 6 கட்டங்கள் தேவை"</string>
<string name="qs_edit" msgid="2232596095725105230">"மாற்று"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index fc48ac1..7c09699 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"వినియోగదారుని లాగ్ అవుట్ చేయి"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"కొత్త వినియోగదారుని జోడించాలా?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"మీరు కొత్త వినియోగదారుని జోడించినప్పుడు, ఆ వ్యక్తి తన స్థలాన్ని సెటప్ చేసుకోవాలి.\n\nఏ వినియోగదారు అయినా మిగతా అందరు వినియోగదారుల కోసం అనువర్తనాలను నవీకరించగలరు."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"వినియోగదారు పరిమితిని చేరుకున్నారు"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">మీరు <xliff:g id="COUNT">%d</xliff:g> వినియోగదారుల వరకు జోడించవచ్చు.</item>
+ <item quantity="one">ఒక్క వినియోగదారుని మాత్రమే సృష్టించవచ్చు.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"వినియోగదారుని తీసివేయాలా?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"ఈ వినియోగదారుకు సంబంధించిన అన్ని అనువర్తనాలు మరియు డేటా తొలగించబడతాయి."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"తీసివేయి"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"ఎడమ వైపు చిహ్నం"</string>
<string name="right_icon" msgid="3952104823293824311">"కుడివైపు ఉన్న చిహ్నం"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"టైల్లను జోడించడం కోసం పట్టుకుని, లాగండి"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"టైల్ల క్రమం మార్చడానికి వాటిని పట్టుకుని, లాగండి"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"తీసివేయడానికి ఇక్కడికి లాగండి"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"మీ వద్ద కనీసం 6 టైల్లు ఉండాలి"</string>
<string name="qs_edit" msgid="2232596095725105230">"సవరించు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index bf61986..1d59868 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -426,14 +426,16 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ออกจากระบบผู้ใช้"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"ต้องการเพิ่มผู้ใช้ใหม่ใช่ไหม"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"เมื่อคุณเพิ่มผู้ใช้ใหม่ ผู้ใช้ดังกล่าวจะต้องตั้งค่าพื้นที่ของตนเอง\n\nผู้ใช้ทุกคนสามารถอัปเดตแอปสำหรับผู้ใช้รายอื่นทุกคนได้"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"ถึงขีดจำกัดผู้ใช้แล้ว"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">คุณเพิ่มผู้ใช้ได้สูงสุด <xliff:g id="COUNT">%d</xliff:g> คน</item>
+ <item quantity="one">สร้างผู้ใช้ได้เพียง 1 คนเท่านั้น</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"นำผู้ใช้ออกใช่ไหม"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"แอปและข้อมูลทั้งหมดของผู้ใช้นี้จะถูกลบ"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"นำออก"</string>
<string name="battery_saver_notification_title" msgid="8614079794522291840">"เปิดโหมดประหยัดแบตเตอรี่อยู่"</string>
- <string name="battery_saver_notification_text" msgid="820318788126672692">"ลดการใช้แบตเตอรี่และข้อมูลแบ็กกราวด์"</string>
+ <string name="battery_saver_notification_text" msgid="820318788126672692">"ลดการใช้แบตเตอรี่และอินเทอร์เน็ตที่ใช้งานอยู่เบื้องหลัง"</string>
<string name="battery_saver_notification_action_text" msgid="132118784269455533">"ปิดโหมดประหยัดแบตเตอรี่"</string>
<string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะเริ่มจับภาพทุกอย่างที่แสดงบนหน้าจอ"</string>
<string name="media_projection_remember_text" msgid="3103510882172746752">"ไม่ต้องแสดงข้อความนี้อีก"</string>
@@ -646,7 +648,7 @@
<string name="battery_panel_title" msgid="7944156115535366613">"การใช้งานแบตเตอรี่"</string>
<string name="battery_detail_charging_summary" msgid="1279095653533044008">"ไม่สามารถใช้โหมดประหยัดแบตเตอรี่ระหว่างการชาร์จ"</string>
<string name="battery_detail_switch_title" msgid="6285872470260795421">"โหมดประหยัดแบตเตอรี่"</string>
- <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ลดประสิทธิภาพการทำงานและข้อมูลแบ็กกราวด์"</string>
+ <string name="battery_detail_switch_summary" msgid="9049111149407626804">"ลดประสิทธิภาพการทำงานและอินเทอร์เน็ตที่ใช้งานอยู่เบื้องหลัง"</string>
<string name="keyboard_key_button_template" msgid="6230056639734377300">"ปุ่ม <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
<string name="keyboard_key_back" msgid="2337450286042721351">"กลับ"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"ไอคอนทางซ้าย"</string>
<string name="right_icon" msgid="3952104823293824311">"ไอคอนทางขวา"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"กดชิ้นส่วนค้างไว้แล้วลากไปเพื่อเพิ่ม"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"กดชิ้นส่วนค้างไว้แล้วลากไปเพื่อจัดลำดับใหม่"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ลากมาที่นี่เพื่อนำออก"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"คุณต้องมีชิ้นส่วนอย่างน้อย 6 ชิ้น"</string>
<string name="qs_edit" msgid="2232596095725105230">"แก้ไข"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index fef7632..013acd4 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"I-LOGOUT ANG USER"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Magdagdag ng bagong user?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Kapag nagdagdag ka ng bagong user, kailangang i-set up ng taong iyon ang kanyang espasyo.\n\nAng sinumang user ay maaaring mag-update ng mga app para sa lahat ng iba pang user."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Naabot na ang limitasyon sa user"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Maaari kang magdagdag ng hanggang <xliff:g id="COUNT">%d</xliff:g> user.</item>
+ <item quantity="other">Maaari kang magdagdag ng hanggang <xliff:g id="COUNT">%d</xliff:g> na user.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Gusto mo bang alisin ang user?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Made-delete ang lahat ng app at data ng user na ito."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Alisin"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Icon ng kaliwa"</string>
<string name="right_icon" msgid="3952104823293824311">"Icon ng kanan"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Pindutin nang matagal at i-drag para magdagdag ng mga tile"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Pindutin nang matagal at i-drag para ayusing muli ang tile"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"I-drag dito upang alisin"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Kailangan mo ng hindi bababa sa 6 na tile"</string>
<string name="qs_edit" msgid="2232596095725105230">"I-edit"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index fd8ab73..9030113 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"KULLANICI OTURUMUNU KAPAT"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Yeni kullanıcı eklensin mi?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Yeni bir kullanıcı eklediğinizde, bu kişinin kendi alanını ayarlaması gerekir.\n\nHerhangi bir kullanıcı, diğer tüm kullanıcılar için uygulamaları güncelleyebilir."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Kullanıcı sınırına ulaşıldı"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">En fazla <xliff:g id="COUNT">%d</xliff:g> kullanıcı ekleyebilirsiniz.</item>
+ <item quantity="one">Yalnızca bir kullanıcı oluşturulabilir.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Kullanıcı kaldırılsın mı?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Bu kullanıcının tüm uygulamaları ve verileri silinecek."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Kaldır"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Sol simge"</string>
<string name="right_icon" msgid="3952104823293824311">"Sağ simge"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Blok eklemek için basılı tutup sürükleyin"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Blokları yeniden düzenlemek için basılı tutun ve sürükleyin"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Kaldırmak için buraya sürükleyin"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"En az 6 blok gerekiyor"</string>
<string name="qs_edit" msgid="2232596095725105230">"Düzenle"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 7d61d37..cf21c0f 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -434,9 +434,13 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ВИЙТИ З ОБЛІКОВОГО ЗАПИСУ"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Додати нового користувача?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Користувач має налаштувати свій профіль після створення.\n\nБудь-який користувач пристрою може оновлювати додатки для решти користувачів."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Ви досягли ліміту користувачів"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувача.</item>
+ <item quantity="few">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувачів.</item>
+ <item quantity="many">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувачів.</item>
+ <item quantity="other">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувача.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Видалити користувача?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Усі додатки й дані цього користувача буде видалено."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Видалити"</string>
@@ -744,8 +748,7 @@
<string name="left_icon" msgid="3096287125959387541">"Значок ліворуч"</string>
<string name="right_icon" msgid="3952104823293824311">"Значок праворуч"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Натисніть і перетягніть, щоб додати фрагменти"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Щоб упорядковувати компоненти, перетягуйте їх"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Перетягніть сюди, щоб видалити"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Потрібно принаймні 6 фрагментів"</string>
<string name="qs_edit" msgid="2232596095725105230">"Редагувати"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index dbdc034..d2ee8c7 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"صارف لاگ آؤٹ کریں"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"نیا صارف شامل کریں؟"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"جب آپ ایک نیا صارف شامل کرتے ہیں تو اس شخص کو اپنی جگہ کو ترتیب دینے کی ضرورت ہوتی ہے۔\n\nکوئی بھی صارف دیگر سبھی صارفین کیلئے ایپس کو اپ ڈیٹ کر سکتا ہے۔"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"صارف کی حد مکمل ہو گئی"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">صرف <xliff:g id="COUNT">%d</xliff:g> صارفین بنائے جا سکتے ہیں۔</item>
+ <item quantity="one">صرف ایک صارف بنایا جا سکتا ہے۔</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"صارف کو ہٹائیں؟"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"اس صارف کی سبھی ایپس اور ڈیٹا حذف کر دیا جائے گا۔"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"ہٹائیں"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"بائیں جانب کا آئیکن"</string>
<string name="right_icon" msgid="3952104823293824311">"دائيں جانب کا آئيکن"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"ٹائلز شامل کرنے کے لئے پکڑ کر گھسیٹیں"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"ٹائلز کو دوبارہ ترتیب دینے کیلئے پکڑ کر گھسیٹیں"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"ہٹانے کیلئے یہاں گھسیٹیں؟"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"آپ کو کم از کم 6 ٹائلز کی ضرورت ہے"</string>
<string name="qs_edit" msgid="2232596095725105230">"ترمیم کریں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index f4403e7..2c45ab8 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"FOYDALANUVCHI NOMIDAN CHIQISH"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Foydalanuvchi qo‘shilsinmi?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Yangi profil qo‘shilgach, uni sozlash lozim.\n\nQurilmaning istalgan foydalanuvchisi ilovalarni barcha hisoblar uchun yangilashi mumkin."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Limitga yetib keldi"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> tagacha foydalanuvchi qo‘shish mumkin.</item>
+ <item quantity="one">Faqat bitta foydalanuvchi yaratish mumkin.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Foydalanuvchi olib tashlansinmi?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Ushbu foydalanuvchining barcha ilovalari va ma’lumotlari o‘chirib tashlanadi."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Olib tashlash"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"Chapga belgisi"</string>
<string name="right_icon" msgid="3952104823293824311">"O‘ngga belgisi"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Keraklisini ushlab torting"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Qayta tartiblash uchun ushlab torting"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"O‘chirish uchun bu yerga torting"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Kamida 6 ta katakcha lozim"</string>
<string name="qs_edit" msgid="2232596095725105230">"Tahrirlash"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index d8fd0eb..8c5e10d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -204,8 +204,8 @@
<string name="accessibility_quick_settings_wifi_changed_on" msgid="6440117170789528622">"Đã bật Wifi."</string>
<string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"Di động <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string>
<string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"Pin <xliff:g id="STATE">%s</xliff:g>."</string>
- <string name="accessibility_quick_settings_airplane_off" msgid="7786329360056634412">"Chế độ trên máy bay tắt."</string>
- <string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"Chế độ trên máy bay bật."</string>
+ <string name="accessibility_quick_settings_airplane_off" msgid="7786329360056634412">"Chế độ trên máy bay đang tắt."</string>
+ <string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"Chế độ trên máy bay đang bật."</string>
<string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"Đã tắt chế độ trên máy bay."</string>
<string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"Đã bật chế độ trên máy bay."</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="2960643943620637020">"hoàn toàn tắt tiếng"</string>
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"ĐĂNG XUẤT NGƯỜI DÙNG"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Thêm người dùng mới?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Khi bạn thêm người dùng mới, người dùng đó cần thiết lập dung lượng lưu trữ của mình.\n\nMọi người dùng đều có thể cập nhật ứng dụng cho tất cả người dùng khác."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Đã đạt đến giới hạn người dùng"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">Bạn có thể thêm tối đa <xliff:g id="COUNT">%d</xliff:g> người dùng.</item>
+ <item quantity="one">Chỉ có thể tạo một người dùng.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Xóa người dùng?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Tất cả các ứng dụng và dữ liệu của người dùng này sẽ bị xóa."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Xóa"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Biểu tượng bên trái"</string>
<string name="right_icon" msgid="3952104823293824311">"Biểu tượng bên phải"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Giữ và kéo để thêm ô"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Giữ và kéo để sắp xếp lại các ô"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Kéo vào đây để xóa"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Bạn cần ít nhất 6 ô"</string>
<string name="qs_edit" msgid="2232596095725105230">"Chỉnh sửa"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index c7443fe..6259bfd 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"退出当前用户"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"要添加新用户吗?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"当您添加新用户时,该用户必须设置自己的空间。\n\n任何用户均可为其他所有用户更新应用。"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"已达到用户数上限"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">您最多可以添加 <xliff:g id="COUNT">%d</xliff:g> 位用户。</item>
+ <item quantity="one">您只能创建一位用户。</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"是否移除用户?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"此用户的所有应用和数据均将被删除。"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"移除"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"向左图标"</string>
<string name="right_icon" msgid="3952104823293824311">"向右图标"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"按住并拖动即可添加图块"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"按住并拖动即可重新排列图块"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"拖动到此处即可移除"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"您至少需要 6 个图块"</string>
<string name="qs_edit" msgid="2232596095725105230">"编辑"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index dace26a..aa19f4f 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -428,9 +428,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"登出使用者"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"新增使用者?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"新增的使用者需要自行設定個人空間。\n\n任何使用者均可為所有其他使用者更新應用程式。"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"已達到使用者上限"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">您可以加入多達 <xliff:g id="COUNT">%d</xliff:g> 個使用者。</item>
+ <item quantity="one">只可以建立一個使用者。</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"移除使用者?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"系統將會刪除這個使用者的所有應用程式和資料。"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"移除"</string>
@@ -734,8 +736,7 @@
<string name="left_icon" msgid="3096287125959387541">"向左圖示"</string>
<string name="right_icon" msgid="3952104823293824311">"向右圖示"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"按住並拖曳即可新增圖塊"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"按住並拖曳即可重新排列圖塊"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"拖曳這裡即可移除"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"您必須至少有 6 個圖塊"</string>
<string name="qs_edit" msgid="2232596095725105230">"編輯"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index c1be1fb..363c610 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"登出使用者"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"新增使用者?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"新增的使用者需要自行設定個人空間。\n\n任何使用者皆可為其他所有使用者更新應用程式。"</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"已達使用者數量上限"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="other">最多可新增 <xliff:g id="COUNT">%d</xliff:g> 位使用者。</item>
+ <item quantity="one">只能建立 1 位使用者。</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"要移除使用者嗎?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"系統將刪除這個使用者的所有應用程式和資料。"</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"移除"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"向左圖示"</string>
<string name="right_icon" msgid="3952104823293824311">"向右圖示"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"按住並拖曳即可新增圖塊"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"按住並拖曳即可重新排列圖塊"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"拖曳到這裡即可移除"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"你至少必須要有 6 個圖塊"</string>
<string name="qs_edit" msgid="2232596095725105230">"編輯"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 5be16e3..0e4729b 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -426,9 +426,11 @@
<string name="user_logout_notification_action" msgid="1195428991423425062">"KHIPHA UMSEBENZISI"</string>
<string name="user_add_user_title" msgid="4553596395824132638">"Engeza umsebenzisi omusha?"</string>
<string name="user_add_user_message_short" msgid="2161624834066214559">"Uma ungeza umsebenzisi omusha, loyo muntu udinga ukusetha isikhala sakhe.\n\nNoma yimuphi umsebenzisi angabuyekeza izinhlelo zokusebenza kubo bonke abasebenzisi."</string>
- <!-- no translation found for user_limit_reached_title (7374910700117359177) -->
- <skip />
- <!-- no translation found for user_limit_reached_message (1855040563671964242) -->
+ <string name="user_limit_reached_title" msgid="7374910700117359177">"Kufinyelelwe kumkhawulo womsebenzisi"</string>
+ <plurals name="user_limit_reached_message" formatted="false" msgid="1855040563671964242">
+ <item quantity="one">Ungangeza kufikela kubasebenzisi abangu-<xliff:g id="COUNT">%d</xliff:g>.</item>
+ <item quantity="other">Ungangeza kufikela kubasebenzisi abangu-<xliff:g id="COUNT">%d</xliff:g>.</item>
+ </plurals>
<string name="user_remove_user_title" msgid="4681256956076895559">"Susa umsebenzisi?"</string>
<string name="user_remove_user_message" msgid="1453218013959498039">"Zonke izinhlelo zokusebenza nedatha yalo msebenzisi kuzosuswa."</string>
<string name="user_remove_user_remove" msgid="7479275741742178297">"Susa"</string>
@@ -732,8 +734,7 @@
<string name="left_icon" msgid="3096287125959387541">"Isithonjana esingakwesokunxele"</string>
<string name="right_icon" msgid="3952104823293824311">"Isithonjana sangakwesokudla"</string>
<string name="drag_to_add_tiles" msgid="230586591689084925">"Bamba uphinde uhudule ukuze ungeze amathayela"</string>
- <!-- no translation found for drag_to_rearrange_tiles (4566074720193667473) -->
- <skip />
+ <string name="drag_to_rearrange_tiles" msgid="4566074720193667473">"Bamba uphinde uhudule ukuze uphinde ulungise amathayela"</string>
<string name="drag_to_remove_tiles" msgid="3361212377437088062">"Hudulela lapha ukuze ususe"</string>
<string name="drag_to_remove_disabled" msgid="2390968976638993382">"Udinga okungenani amathayela angu-6"</string>
<string name="qs_edit" msgid="2232596095725105230">"Hlela"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 19492a0..11bd392 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -79,13 +79,6 @@
<!-- Show camera affordance on Keyguard -->
<bool name="config_keyguardShowCameraAffordance">false</bool>
- <!-- Whether we should use SRC drawing mode when drawing the scrim behind. If this flag is set,
- we change the canvas opacity so libhwui doesn't call glClear on our surface, and then we
- draw the scrim with SRC to overwrite the whole buffer, which saves us a layer of overdraw.
- However, SRC performs poorly on some devices, where it is more efficient to
- glClear + SRC_OVER, in which case this flag should be disabled. -->
- <bool name="config_status_bar_scrim_behind_use_src">true</bool>
-
<!-- The length of the vibration when the notification pops open. -->
<integer name="one_finger_pop_duration_ms">10</integer>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d051f07..26eadb5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -361,7 +361,7 @@
<dimen name="qs_quick_layout_width">-1px</dimen>
<dimen name="qs_quick_tile_padding">12dp</dimen>
<dimen name="qs_header_gear_translation">16dp</dimen>
- <dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
+ <dimen name="qs_header_tile_margin_horizontal">4dp</dimen>
<dimen name="qs_page_indicator_width">16dp</dimen>
<dimen name="qs_page_indicator_height">8dp</dimen>
<dimen name="qs_tile_icon_size">24dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index 0d25c91..0cde9da 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -16,6 +16,7 @@
package com.android.systemui.shared.system;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
@@ -146,8 +147,9 @@
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
- mWindowManager.destroyInputConsumer(mName);
- mWindowManager.createInputConsumer(mToken, mName, inputChannel);
+ // TODO(b/113087003): Support Picture-in-picture in multi-display.
+ mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
+ mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
} catch (RemoteException e) {
Log.e(TAG, "Failed to create input consumer", e);
}
@@ -164,7 +166,8 @@
public void unregisterInputConsumer() {
if (mInputEventReceiver != null) {
try {
- mWindowManager.destroyInputConsumer(mName);
+ // TODO(b/113087003): Support Picture-in-picture in multi-display.
+ mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
} catch (RemoteException e) {
Log.e(TAG, "Failed to destroy input consumer", e);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
index 5605b7a..ac8f024 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/EmergencyButton.java
@@ -39,6 +39,7 @@
import com.android.internal.telephony.IccCardConstants.State;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.systemui.util.EmergencyDialerConstants;
/**
* This class implements a smart emergency button that updates itself based
@@ -48,11 +49,13 @@
*/
public class EmergencyButton extends Button {
private static final Intent INTENT_EMERGENCY_DIAL = new Intent()
- .setAction("com.android.phone.EmergencyDialer.DIAL")
+ .setAction(EmergencyDialerConstants.ACTION_DIAL)
.setPackage("com.android.phone")
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ .putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+ EmergencyDialerConstants.ENTRY_TYPE_LOCKSCREEN_BUTTON);
private static final String LOG_TAG = "EmergencyButton";
private final EmergencyAffordanceManager mEmergencyAffordanceManager;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 51cc4a1..e6026c1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
- private static final int PULSE_REASONS = 7;
+ private static final int REASONS = 8;
public static final int PULSE_REASON_NONE = -1;
public static final int PULSE_REASON_INTENT = 0;
@@ -45,6 +45,7 @@
public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
public static final int PULSE_REASON_SENSOR_REACH = 6;
+ public static final int REASON_SENSOR_WAKE_UP = 7;
private static boolean sRegisterKeyguardCallback = true;
@@ -74,7 +75,7 @@
public static void tracePulseStart(int reason) {
if (!ENABLED) return;
sPulsing = true;
- log("pulseStart reason=" + pulseReasonToString(reason));
+ log("pulseStart reason=" + reasonToString(reason));
}
public static void tracePulseFinish() {
@@ -102,8 +103,8 @@
sScreenOnPulsingStats = new SummaryStats();
sScreenOnNotPulsingStats = new SummaryStats();
sEmergencyCallStats = new SummaryStats();
- sProxStats = new SummaryStats[PULSE_REASONS][2];
- for (int i = 0; i < PULSE_REASONS; i++) {
+ sProxStats = new SummaryStats[REASONS][2];
+ for (int i = 0; i < REASONS; i++) {
sProxStats[i][0] = new SummaryStats();
sProxStats[i][1] = new SummaryStats();
}
@@ -157,6 +158,12 @@
log("missedTick by=" + delay);
}
+ public static void traceTimeTickScheduled(long when, long triggerAt) {
+ if (!ENABLED) return;
+ log("timeTickScheduled at=" + FORMAT.format(new Date(when)) + " triggerAt="
+ + FORMAT.format(new Date(triggerAt)));
+ }
+
public static void traceKeyguard(boolean showing) {
if (!ENABLED) return;
log("keyguard " + showing);
@@ -176,15 +183,15 @@
}
public static void traceProximityResult(Context context, boolean near, long millis,
- int pulseReason) {
+ int reason) {
if (!ENABLED) return;
init(context);
- log("proximityResult reason=" + pulseReasonToString(pulseReason) + " near=" + near
+ log("proximityResult reason=" + reasonToString(reason) + " near=" + near
+ " millis=" + millis);
- sProxStats[pulseReason][near ? 0 : 1].append();
+ sProxStats[reason][near ? 0 : 1].append();
}
- public static String pulseReasonToString(int pulseReason) {
+ public static String reasonToString(int pulseReason) {
switch (pulseReason) {
case PULSE_REASON_INTENT: return "intent";
case PULSE_REASON_NOTIFICATION: return "notification";
@@ -193,6 +200,7 @@
case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
case PULSE_REASON_SENSOR_REACH: return "reach";
+ case REASON_SENSOR_WAKE_UP: return "wakeup";
default: throw new IllegalArgumentException("bad reason: " + pulseReason);
}
}
@@ -218,8 +226,8 @@
sScreenOnPulsingStats.dump(pw, "Screen on (pulsing)");
sScreenOnNotPulsingStats.dump(pw, "Screen on (not pulsing)");
sEmergencyCallStats.dump(pw, "Emergency call");
- for (int i = 0; i < PULSE_REASONS; i++) {
- final String reason = pulseReasonToString(i);
+ for (int i = 0; i < REASONS; i++) {
+ final String reason = reasonToString(i);
sProxStats[i][0].dump(pw, "Proximity near (" + reason + ")");
sProxStats[i][1].dump(pw, "Proximity far (" + reason + ")");
}
@@ -262,10 +270,10 @@
}
}
- public static void traceSensor(Context context, int pulseReason) {
+ public static void traceSensor(Context context, int reason) {
if (!ENABLED) return;
init(context);
- log("sensor type=" + pulseReasonToString(pulseReason));
+ log("sensor type=" + reasonToString(reason));
}
private static class SummaryStats {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 045a98c..f9dfb5d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -63,6 +63,7 @@
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
private final Consumer<Boolean> mProxCallback;
+ private final Consumer<Boolean> mWakeScreenCallback;
private final Callback mCallback;
private final Handler mHandler = new Handler();
@@ -70,9 +71,9 @@
public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager,
- DozeParameters dozeParameters,
- AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback,
- Consumer<Boolean> proxCallback, AlwaysOnDisplayPolicy policy) {
+ DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
+ Callback callback, Consumer<Boolean> proxCallback,
+ Consumer<Boolean> wakeScreenCallback, AlwaysOnDisplayPolicy policy) {
mContext = context;
mAlarmManager = alarmManager;
mSensorManager = sensorManager;
@@ -80,6 +81,7 @@
mConfig = config;
mWakeLock = wakeLock;
mProxCallback = proxCallback;
+ mWakeScreenCallback = wakeScreenCallback;
mResolver = mContext.getContentResolver();
mSensors = new TriggerSensor[] {
@@ -117,6 +119,7 @@
DozeLog.PULSE_REASON_SENSOR_REACH,
false /* reports touch coordinates */,
false /* touchscreen */),
+ new WakeScreenSensor(),
};
mProxSensor = new ProxSensor(policy);
@@ -302,9 +305,9 @@
final boolean mSettingDefault;
final boolean mRequiresTouchscreen;
- private boolean mRequested;
- private boolean mRegistered;
- private boolean mDisabled;
+ protected boolean mRequested;
+ protected boolean mRegistered;
+ protected boolean mDisabled;
public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason,
boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
@@ -348,7 +351,7 @@
}
}
- private boolean enabledBySetting() {
+ protected boolean enabledBySetting() {
if (TextUtils.isEmpty(mSetting)) {
return true;
}
@@ -401,7 +404,7 @@
}
}
- private String triggerEventToString(TriggerEvent event) {
+ protected String triggerEventToString(TriggerEvent event) {
if (event == null) return null;
final StringBuilder sb = new StringBuilder("TriggerEvent[")
.append(event.timestamp).append(',')
@@ -415,6 +418,28 @@
}
}
+ private class WakeScreenSensor extends TriggerSensor {
+
+ WakeScreenSensor() {
+ super(findSensorWithType(mConfig.wakeScreenSensorType()),
+ Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, true /* configured */,
+ DozeLog.REASON_SENSOR_WAKE_UP, false /* reportsTouchCoordinates */,
+ false /* requiresTouchscreen */);
+ }
+
+ @Override
+ @AnyThread
+ public void onTrigger(TriggerEvent event) {
+ DozeLog.traceSensor(mContext, mPulseReason);
+ mHandler.post(mWakeLock.wrap(() -> {
+ if (DEBUG) Log.d(TAG, "onTrigger: " + triggerEventToString(event));
+ mRegistered = false;
+ mWakeScreenCallback.accept(event.values[0] > 0);
+ updateListener(); // reregister, this sensor only fires once
+ }));
+ }
+ }
+
public interface Callback {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 73cbd7d..1589969 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -84,7 +84,7 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
- config, wakeLock, this::onSensor, this::onProximityFar,
+ config, wakeLock, this::onSensor, this::onProximityFar, this::onWakeScreen,
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
@@ -103,7 +103,7 @@
private void proximityCheckThenCall(IntConsumer callback,
boolean alreadyPerformedProxCheck,
- int pulseReason) {
+ int reason) {
Boolean cachedProxFar = mDozeSensors.isProximityCurrentlyFar();
if (alreadyPerformedProxCheck) {
callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
@@ -116,7 +116,7 @@
public void onProximityResult(int result) {
final long end = SystemClock.uptimeMillis();
DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
- end - start, pulseReason);
+ end - start, reason);
callback.accept(result);
}
}.check();
@@ -182,6 +182,28 @@
}
}
+ private void onWakeScreen(boolean wake) {
+ DozeMachine.State state = mMachine.getState();
+ boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
+ boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
+
+ if (wake) {
+ proximityCheckThenCall((result) -> {
+ if (result == ProximityCheck.RESULT_NEAR) {
+ // In pocket, drop event.
+ return;
+ }
+ if (pausing || paused) {
+ mMachine.requestState(DozeMachine.State.DOZE_AOD);
+ }
+ }, false /* alreadyPerformedProxCheck */, DozeLog.REASON_SENSOR_WAKE_UP);
+ } else {
+ if (!pausing && !paused) {
+ mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSING);
+ }
+ }
+ }
+
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 7863245..67aa82d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -158,8 +158,12 @@
return;
}
- long delta = roundToNextMinute(System.currentTimeMillis()) - System.currentTimeMillis();
- mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ long time = System.currentTimeMillis();
+ long delta = roundToNextMinute(time) - System.currentTimeMillis();
+ boolean scheduled = mTimeTicker.schedule(delta, AlarmTimeout.MODE_IGNORE_IF_SCHEDULED);
+ if (scheduled) {
+ DozeLog.traceTimeTickScheduled(time, time + delta);
+ }
mLastTimeTickElapsed = SystemClock.elapsedRealtime();
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 06d4434..cc1b9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -89,6 +89,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.util.EmergencyDialerConstants;
import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
import java.util.ArrayList;
@@ -449,9 +450,6 @@
}
private class EmergencyDialerAction extends SinglePressAction {
- private static final String ACTION_EMERGENCY_DIALER_DIAL =
- "com.android.phone.EmergencyDialer.DIAL";
-
private EmergencyDialerAction() {
super(R.drawable.ic_faster_emergency,
R.string.global_action_emergency);
@@ -460,8 +458,10 @@
@Override
public void onPress() {
MetricsLogger.action(mContext, MetricsEvent.ACTION_EMERGENCY_DIALER_FROM_POWER_MENU);
- Intent intent = new Intent(ACTION_EMERGENCY_DIALER_DIAL);
+ Intent intent = new Intent(EmergencyDialerConstants.ACTION_DIAL);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(EmergencyDialerConstants.EXTRA_ENTRY_TYPE,
+ EmergencyDialerConstants.ENTRY_TYPE_POWER_MENU);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4977ff7..1655c01 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -503,9 +503,6 @@
case READY:
synchronized (KeyguardViewMediator.this) {
if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
- if (mShowing) {
- resetStateLocked();
- }
mLockWhenSimRemoved = true;
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
index e7eefe8..376e6ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
@@ -43,9 +43,9 @@
R.dimen.qs_tile_icon_size));
}
- protected void updateIcon(ImageView iv, State state) {
+ protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
if (!(state.icon instanceof SignalIcon)) {
- super.updateIcon(iv, state);
+ super.updateIcon(iv, state, allowAnimations);
return;
} else if (!Objects.equals(state.icon, iv.getTag(R.id.qs_icon_tag))) {
mSignalDrawable.setLevel(((SignalIcon) state.icon).getState());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index f1b7eec..ca1b489 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -261,6 +261,12 @@
return mPages.get(0).mColumns;
}
+ public int getNumVisibleTiles() {
+ if (mPages.size() == 0) return 0;
+ TilePage currentPage = mPages.get(getCurrentItem());
+ return currentPage.mRecords.size();
+ }
+
public void startTileReveal(Set<String> tileSpecs, final Runnable postAnimation) {
if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0 || !beginFakeDrag()) {
// Do not start the reveal animation unless there are tiles to animate, multiple
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 2a4bb60..f607367 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -169,6 +169,7 @@
QSTileLayout tileLayout = mQsPanel.getTileLayout();
mAllViews.add((View) tileLayout);
int height = mQs.getView() != null ? mQs.getView().getMeasuredHeight() : 0;
+ int width = mQs.getView() != null ? mQs.getView().getMeasuredWidth() : 0;
int heightDiff = height - mQs.getHeader().getBottom()
+ mQs.getHeader().getPaddingBottom();
firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0);
@@ -181,7 +182,9 @@
}
final View tileIcon = tileView.getIcon().getIconView();
View view = mQs.getView();
- if (count < mNumQuickTiles && mAllowFancy) {
+
+ // This case: less tiles to animate in small displays.
+ if (count < mQuickQsPanel.getTileLayout().getNumVisibleTiles() && mAllowFancy) {
// Quick tiles.
QSTileView quickTileView = mQuickQsPanel.getTileView(tile);
if (quickTileView == null) continue;
@@ -192,18 +195,26 @@
final int xDiff = loc2[0] - loc1[0];
final int yDiff = loc2[1] - loc1[1];
lastXDiff = loc1[0] - lastX;
- // Move the quick tile right from its location to the new one.
- translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
- translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
- // Counteract the parent translation on the tile. So we have a static base to
- // animate the label position off from.
- //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
+ if (count < tileLayout.getNumVisibleTiles()) {
+ // Move the quick tile right from its location to the new one.
+ translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff);
+ translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
- // Move the real tile from the quick tile position to its final
- // location.
- translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
- translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
+ // Counteract the parent translation on the tile. So we have a static base to
+ // animate the label position off from.
+ //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0);
+
+ // Move the real tile from the quick tile position to its final
+ // location.
+ translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0);
+ translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0);
+
+ } else { // These tiles disappear when expanding
+ firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0);
+ translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff);
+ translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff + width);
+ }
mQuickQsViews.add(tileView.getIconWithBackground());
mAllViews.add(tileView.getIcon());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 7cb22a3..c36cdf6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -101,12 +101,7 @@
if (savedInstanceState != null) {
setExpanded(savedInstanceState.getBoolean(EXTRA_EXPANDED));
setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
- int[] loc = new int[2];
- View edit = view.findViewById(android.R.id.edit);
- edit.getLocationInWindow(loc);
- int x = loc[0] + edit.getWidth() / 2;
- int y = loc[1] + edit.getHeight() / 2;
- mQSCustomizer.setEditLocation(x, y);
+ setEditLocation(view);
mQSCustomizer.restoreInstanceState(savedInstanceState);
}
SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
@@ -161,15 +156,24 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ setEditLocation(getView());
if (newConfig.getLayoutDirection() != mLayoutDirection) {
mLayoutDirection = newConfig.getLayoutDirection();
-
if (mQSAnimator != null) {
mQSAnimator.onRtlChanged();
}
}
}
+ private void setEditLocation(View view) {
+ Log.w(TAG, "I'm changing the location of the button!!!");
+ View edit = view.findViewById(android.R.id.edit);
+ int[] loc = edit.getLocationOnScreen();
+ int x = loc[0] + edit.getWidth() / 2;
+ int y = loc[1] + edit.getHeight() / 2;
+ mQSCustomizer.setEditLocation(x, y);
+ }
+
@Override
public void setContainer(ViewGroup container) {
if (container instanceof NotificationsQuickSettingsContainer) {
@@ -284,6 +288,7 @@
mHeader.setListening(listening);
mFooter.setListening(listening);
mQSPanel.setListening(mListening && mQsExpanded);
+ mQSPanel.getFooter().setListening(listening);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 3fc258b..7a57fdd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -29,6 +29,7 @@
import android.os.Message;
import android.service.quicksettings.Tile;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
@@ -60,6 +61,8 @@
public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
public static final String QS_SHOW_HEADER = "qs_show_header";
+ private static final String TAG = "QSPanel";
+
protected final Context mContext;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
protected final View mBrightnessView;
@@ -313,7 +316,7 @@
public void onCollapse() {
if (mCustomizePanel != null && mCustomizePanel.isShown()) {
- mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
+ mCustomizePanel.hide();
}
}
@@ -347,7 +350,6 @@
if (mTileLayout != null) {
mTileLayout.setListening(listening);
}
- mFooter.setListening(mListening);
if (mListening) {
refreshAllTiles();
}
@@ -480,8 +482,7 @@
public void run() {
if (mCustomizePanel != null) {
if (!mCustomizePanel.isCustomizing()) {
- int[] loc = new int[2];
- v.getLocationInWindow(loc);
+ int[] loc = v.getLocationOnScreen();
int x = loc[0] + v.getWidth() / 2;
int y = loc[1] + v.getHeight() / 2;
mCustomizePanel.show(x, y);
@@ -495,7 +496,7 @@
public void closeDetail() {
if (mCustomizePanel != null && mCustomizePanel.isShown()) {
// Treat this as a detail panel for now, to make things easy.
- mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
+ mCustomizePanel.hide();
return;
}
showDetail(false, mDetailRecord);
@@ -663,5 +664,7 @@
void setListening(boolean listening);
default void setExpansion(float expansion) {}
+
+ int getNumVisibleTiles();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 6418c80..8f3a7b3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -96,6 +96,7 @@
public void setListening(boolean listening) {
if (listening) {
mSecurityController.addCallback(mCallback);
+ refreshState();
} else {
mSecurityController.removeCallback(mCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 1c50f79..556786a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -18,18 +18,17 @@
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
-import android.widget.Space;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -43,6 +42,7 @@
public class QuickQSPanel extends QSPanel {
public static final String NUM_QUICK_TILES = "sysui_qqs_count";
+ private static final String TAG = "QuickQSPanel";
private boolean mDisabledByPolicy;
private static int mDefaultMaxTiles;
@@ -178,121 +178,95 @@
super.setVisibility(visibility);
}
- private static class HeaderTileLayout extends LinearLayout implements QSTileLayout {
+ private static class HeaderTileLayout extends TileLayout {
- protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private boolean mListening;
- /** Size of the QS tile (width & height). */
- private int mTileDimensionSize;
public HeaderTileLayout(Context context) {
super(context);
setClipChildren(false);
setClipToPadding(false);
-
- mTileDimensionSize = mContext.getResources().getDimensionPixelSize(
- R.dimen.qs_quick_tile_size);
- updateLayoutParams();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- updateLayoutParams();
+ updateResources();
+ }
+
+ @Override
+ public void onFinishInflate(){
+ updateResources();
}
private void updateLayoutParams() {
- setGravity(Gravity.CENTER);
int width = getResources().getDimensionPixelSize(R.dimen.qs_quick_layout_width);
- LayoutParams lp = new LayoutParams(width, LayoutParams.MATCH_PARENT);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(width, LayoutParams.MATCH_PARENT);
lp.gravity = Gravity.CENTER_HORIZONTAL;
setLayoutParams(lp);
}
- /**
- * Returns {@link LayoutParams} based on the given {@code spaceWidth}. If the width is 0,
- * then we're going to have the space expand to take up as much space as possible. If the
- * width is non-zero, we want the inter-tile spacers to be fixed.
- */
- private LayoutParams generateSpaceLayoutParams() {
- LayoutParams lp = new LayoutParams(0, mTileDimensionSize);
- lp.weight = 1;
- lp.gravity = Gravity.CENTER;
- return lp;
- }
-
- @Override
- public void setListening(boolean listening) {
- if (mListening == listening) return;
- mListening = listening;
- for (TileRecord record : mRecords) {
- record.tile.setListening(this, mListening);
- }
- }
-
- @Override
- public void addTile(TileRecord tile) {
- if (getChildCount() != 0) {
- addView(new Space(mContext), getChildCount(), generateSpaceLayoutParams());
- }
-
- addView(tile.tileView, getChildCount(), generateTileLayoutParams());
- mRecords.add(tile);
- tile.tile.setListening(this, mListening);
- }
-
private LayoutParams generateTileLayoutParams() {
- LayoutParams lp = new LayoutParams(mTileDimensionSize, mTileDimensionSize);
- lp.gravity = Gravity.CENTER;
+ LayoutParams lp = new LayoutParams(mCellWidth, mCellHeight);
return lp;
}
@Override
- public void removeTile(TileRecord tile) {
- int childIndex = getChildIndex(tile.tileView);
- // Remove the tile.
- removeViewAt(childIndex);
- if (getChildCount() != 0) {
- // Remove its spacer as well.
- removeViewAt(childIndex);
- }
- mRecords.remove(tile);
- tile.tile.setListening(this, false);
- }
-
- private int getChildIndex(QSTileView tileView) {
- final int childViewCount = getChildCount();
- for (int i = 0; i < childViewCount; i++) {
- if (getChildAt(i) == tileView) {
- return i;
- }
- }
- return -1;
+ protected void addTileView(TileRecord tile) {
+ addView(tile.tileView, getChildCount(), generateTileLayoutParams());
}
@Override
- public int getOffsetTop(TileRecord tile) {
- return 0;
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // We only care about clipping on the right side
+ Rect bounds = new Rect(0, 0, r - l, 10000);
+ setClipBounds(bounds);
+
+ calculateColumns();
+
+ for (int i = 0; i < mRecords.size(); i++) {
+ mRecords.get(i).tileView.setVisibility( i < mColumns ? View.VISIBLE : View.GONE);
+ }
+
+ setAccessibilityOrder();
+ layoutTileRecords(mColumns);
}
@Override
public boolean updateResources() {
- // No resources here.
+ mCellWidth = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
+ mCellHeight = mCellWidth;
+
+ updateLayoutParams();
+
return false;
}
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
+ private boolean calculateColumns() {
+ int prevNumColumns = mColumns;
+ int maxTiles = mRecords.size();
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (hideOverflowingChildren(widthMeasureSpec)) {
- return; // Rely on visibility change to trigger remeasure.
+ if (maxTiles == 0){ // Early return during setup
+ mColumns = 0;
+ return true;
}
+ final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
+ final int leftoverWithespace = availableWidth - maxTiles * mCellWidth;
+ final int smallestHorizontalMarginNeeded = leftoverWithespace / (maxTiles - 1);
+
+ if (smallestHorizontalMarginNeeded > 0){
+ mCellMarginHorizontal = smallestHorizontalMarginNeeded;
+ mColumns = maxTiles;
+ } else{
+ mColumns = mCellWidth == 0 ? 1 :
+ Math.min(maxTiles, availableWidth / mCellWidth );
+ mCellMarginHorizontal = (availableWidth - mColumns * mCellWidth) / (mColumns - 1);
+ }
+ return mColumns != prevNumColumns;
+ }
+
+ private void setAccessibilityOrder() {
if (mRecords != null && mRecords.size() > 0) {
View previousView = this;
for (TileRecord record : mRecords) {
@@ -306,31 +280,28 @@
}
}
- /**
- * Hide child views that would otherwise be clipped.
- * @return {@code true} if any child visibilities have changed.
- */
- private boolean hideOverflowingChildren(int widthMeasureSpec) {
- if (getChildCount() == 0) {
- return false;
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Measure each QS tile.
+ for (TileRecord record : mRecords) {
+ if (record.tileView.getVisibility() == GONE) continue;
+ record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
}
- boolean childVisibilityChanged = false;
- int widthRemaining = MeasureSpec.getSize(widthMeasureSpec)
- - getChildAt(0).getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
- for (int i = 2; i < getChildCount(); i += 2) {
- View tileChild = getChildAt(i);
- LayoutParams lp = (LayoutParams) tileChild.getLayoutParams();
- // All Space views have 0 width; only tiles contribute to the total width.
- widthRemaining = widthRemaining
- - tileChild.getMeasuredWidth() - lp.getMarginEnd() - lp.getMarginStart();
- int newVisibility = widthRemaining < 0 ? View.GONE : View.VISIBLE;
- if (tileChild.getVisibility() != newVisibility) {
- tileChild.setVisibility(newVisibility);
- getChildAt(i - 1).setVisibility(newVisibility); // Hide spacer as well.
- childVisibilityChanged = true;
- }
- }
- return childVisibilityChanged;
+
+ int height = mCellHeight;
+ if (height < 0) height = 0;
+
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height);
+ }
+
+ @Override
+ public int getNumVisibleTiles() {
+ return mColumns;
+ }
+
+ @Override
+ protected int getColumnStart(int column) {
+ return getPaddingStart() + column * (mCellWidth + mCellMarginHorizontal);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index ff5ac76..b988c55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -547,38 +547,23 @@
/**
* Fades in the updated status text. Note that if there's already a status showing, this will
- * immediately hide it and fade in the updated status.
+ * immediately fade it out and fade in the updated status.
*/
private void showStatus() {
mStatusContainer.setAlpha(0f);
- mStatusContainer.setVisibility(View.VISIBLE);
- // Animate the alarm back in. Make sure to clear the animator listener for the animation!
mStatusContainer.animate()
.alpha(1f)
.setDuration(FADE_ANIMATION_DURATION_MS)
- .setListener(null)
.start();
}
- /** Fades out and hides the status text. */
+ /** Fades out the status text. */
private void hideStatusText() {
- if (mStatusContainer.getVisibility() == View.VISIBLE) {
- mStatusContainer.animate()
- .alpha(0f)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (DEBUG) Log.d(TAG, "hideAlarmText: Hid alarm text");
-
- // Reset the alpha regardless of how the animation ends for the next
- // time we show this view/want to animate it.
- mStatusContainer.setVisibility(View.INVISIBLE);
- mStatusContainer.setAlpha(1f);
- }
- })
- .start();
- }
+ mStatusContainer.animate()
+ .alpha(0f)
+ .setDuration(FADE_ANIMATION_DURATION_MS)
+ .start();
}
public void updateEverything() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
index d9583af..ce90fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java
@@ -18,14 +18,13 @@
import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.systemui.R;
-import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.SlashImageView;
@@ -119,9 +118,9 @@
}
@Override
- public void setIcon(QSTile.State state) {
+ public void setIcon(State state, boolean allowAnimations) {
final SignalState s = (SignalState) state;
- setIcon(mSignal, s);
+ setIcon(mSignal, s, allowAnimations);
if (s.overlayIconId > 0) {
mOverlay.setVisibility(VISIBLE);
@@ -134,9 +133,9 @@
} else {
mSignal.setPaddingRelative(0, 0, 0, 0);
}
- final boolean shown = isShown();
- setVisibility(mIn, shown, s.activityIn);
- setVisibility(mOut, shown, s.activityOut);
+ final boolean shouldAnimate = allowAnimations && isShown();
+ setVisibility(mIn, shouldAnimate, s.activityIn);
+ setVisibility(mOut, shouldAnimate, s.activityOut);
}
private void setVisibility(View view, boolean shown, boolean visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 45d63e0..c67165e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -56,6 +56,10 @@
public void addTile(TileRecord tile) {
mRecords.add(tile);
tile.tile.setListening(this, mListening);
+ addTileView(tile);
+ }
+
+ protected void addTileView(TileRecord tile) {
addView(tile.tileView);
}
@@ -120,19 +124,18 @@
return false;
}
- private static int exactly(int size) {
+ protected static int exactly(int size) {
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
}
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- final int w = getWidth();
+
+ protected void layoutTileRecords(int numRecords) {
final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
int row = 0;
int column = 0;
// Layout each QS tile.
- for (int i = 0; i < mRecords.size(); i++, column++) {
+ for (int i = 0; i < numRecords; i++, column++) {
// If we reached the last column available to layout a tile, wrap back to the next row.
if (column == mColumns) {
column = 0;
@@ -147,12 +150,22 @@
}
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ layoutTileRecords(mRecords.size());
+ }
+
private int getRowTop(int row) {
return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
}
- private int getColumnStart(int column) {
+ protected int getColumnStart(int column) {
return getPaddingStart() + mSidePadding + mCellMarginHorizontal / 2 +
column * (mCellWidth + mCellMarginHorizontal);
}
+
+ @Override
+ public int getNumVisibleTiles() {
+ return mRecords.size();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 2ea15bd..3f7eeb8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -26,6 +26,7 @@
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -63,6 +64,7 @@
private static final int MENU_RESET = Menu.FIRST;
private static final String EXTRA_QS_CUSTOMIZING = "qs_customizing";
+ private static final String TAG = "QSCustomizer";
private final QSDetailClipper mClipper;
private final LightBarController mLightBarController;
@@ -94,7 +96,7 @@
mToolbar.setNavigationOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- hide((int) v.getX() + v.getWidth() / 2, (int) v.getY() + v.getHeight() / 2);
+ hide();
}
});
mToolbar.setOnMenuItemClickListener(this);
@@ -154,16 +156,20 @@
mQs = qs;
}
+ /** Animate and show QSCustomizer panel.
+ * @param x,y Location on screen of {@code edit} button to determine center of animation.
+ */
public void show(int x, int y) {
if (!isShown) {
- mX = x;
- mY = y;
+ int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen();
+ mX = x - containerLocation[0];
+ mY = y - containerLocation[1];
MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
isShown = true;
mOpening = true;
setTileSpecs();
setVisibility(View.VISIBLE);
- mClipper.animateCircularClip(x, y, true, mExpandAnimationListener);
+ mClipper.animateCircularClip(mX, mY, true, mExpandAnimationListener);
queryTiles();
mNotifQsContainer.setCustomizerAnimating(true);
mNotifQsContainer.setCustomizerShowing(true);
@@ -192,7 +198,7 @@
mTileQueryHelper.queryTiles(mHost);
}
- public void hide(int x, int y) {
+ public void hide() {
if (isShown) {
MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
isShown = false;
@@ -278,16 +284,18 @@
});
}
}
-
+ /** @param x,y Location on screen of animation center.
+ */
public void setEditLocation(int x, int y) {
- mX = x;
- mY = y;
+ int containerLocation[] = findViewById(R.id.customize_container).getLocationOnScreen();
+ mX = x - containerLocation[0];
+ mY = y - containerLocation[1];
}
private final Callback mKeyguardCallback = () -> {
if (!isAttachedToWindow()) return;
if (Dependency.get(KeyguardMonitor.class).isShowing() && !mOpening) {
- hide(0, 0);
+ hide();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index e7e756f..9dd5d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -84,16 +84,15 @@
layout(mIcon, iconLeft, top);
}
- public void setIcon(QSTile.State state) {
- setIcon((ImageView) mIcon, state);
+ public void setIcon(State state, boolean allowAnimations) {
+ setIcon((ImageView) mIcon, state, allowAnimations);
}
- protected void updateIcon(ImageView iv, State state) {
+ protected void updateIcon(ImageView iv, State state, boolean allowAnimations) {
final QSTile.Icon icon = state.iconSupplier != null ? state.iconSupplier.get() : state.icon;
if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
|| !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
- boolean shouldAnimate = iv.isShown() && mAnimationEnabled
- && iv.getDrawable() != null;
+ boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
Drawable d = icon != null
? shouldAnimate ? icon.getDrawable(mContext)
: icon.getInvisibleDrawable(mContext) : null;
@@ -128,7 +127,11 @@
}
}
- protected void setIcon(ImageView iv, QSTile.State state) {
+ private boolean shouldAnimate(ImageView iv) {
+ return mAnimationEnabled && iv.isShown() && iv.getDrawable() != null;
+ }
+
+ protected void setIcon(ImageView iv, QSTile.State state, boolean allowAnimations) {
if (state.disabledByPolicy) {
iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
} else {
@@ -137,8 +140,8 @@
if (state.state != mState) {
int color = getColor(state.state);
mState = state.state;
- if (iv.isShown() && mTint != 0) {
- animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state));
+ if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
+ animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
mTint = color;
} else {
if (iv instanceof AlphaControlledSlashImageView) {
@@ -148,10 +151,10 @@
setTint(iv, color);
}
mTint = color;
- updateIcon(iv, state);
+ updateIcon(iv, state, allowAnimations);
}
} else {
- updateIcon(iv, state);
+ updateIcon(iv, state, allowAnimations);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 91afef02..d42127e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -47,6 +47,7 @@
private static final String TAG = "QSTileBaseView";
private final H mHandler = new H();
+ private final int[] mLocInScreen = new int[2];
private final FrameLayout mIconFrame;
protected QSIconView mIcon;
protected RippleDrawable mRipple;
@@ -178,8 +179,9 @@
protected void handleStateChanged(QSTile.State state) {
int circleColor = getCircleColor(state.state);
+ boolean allowAnimations = animationsEnabled();
if (circleColor != mCircleColor) {
- if (mBg.isShown() && animationsEnabled()) {
+ if (allowAnimations) {
ValueAnimator animator = ValueAnimator.ofArgb(mCircleColor, circleColor)
.setDuration(QS_ANIM_LENGTH);
animator.addUpdateListener(animation -> mBg.setImageTintList(ColorStateList.valueOf(
@@ -192,7 +194,7 @@
}
setClickable(state.state != Tile.STATE_UNAVAILABLE);
- mIcon.setIcon(state);
+ mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
mAccessibilityClass = state.expandedAccessibilityClassName;
@@ -205,8 +207,17 @@
}
}
+ /* The view should not be animated if it's not on screen and no part of it is visible.
+ */
protected boolean animationsEnabled() {
- return true;
+ if (!isShown()) {
+ return false;
+ }
+ if (getAlpha() != 1f) {
+ return false;
+ }
+ getLocationOnScreen(mLocInScreen);
+ return mLocInScreen[1] >= -getHeight();
}
private int getCircleColor(int state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index fb4fcd4..9e341e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -168,12 +168,12 @@
}
final Resources r = mContext.getResources();
- state.activityIn = cb.enabled && cb.activityIn;
- state.activityOut = cb.enabled && cb.activityOut;
state.label = r.getString(R.string.mobile_data);
boolean mobileDataEnabled = mDataController.isMobileDataSupported()
&& mDataController.isMobileDataEnabled();
state.value = mobileDataEnabled;
+ state.activityIn = mobileDataEnabled && cb.activityIn;
+ state.activityOut = mobileDataEnabled && cb.activityOut;
state.expandedAccessibilityClassName = Switch.class.getName();
if (cb.noSim) {
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
@@ -231,7 +231,6 @@
}
private static final class CallbackInfo {
- boolean enabled;
boolean airplaneModeEnabled;
String dataContentDescription;
boolean activityIn;
@@ -251,7 +250,6 @@
// Not data sim, don't display.
return;
}
- mInfo.enabled = qsIcon.visible;
mInfo.dataContentDescription = typeContentDescription;
mInfo.activityIn = activityIn;
mInfo.activityOut = activityOut;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index c017104..4516518 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -36,10 +36,20 @@
* remove notifications that appear on screen for a period of time and dismiss themselves at the
* appropriate time. These include heads up notifications and ambient pulses.
*/
-public abstract class AlertingNotificationManager {
+public abstract class AlertingNotificationManager implements NotificationLifetimeExtender {
private static final String TAG = "AlertNotifManager";
protected final Clock mClock = new Clock();
protected final ArrayMap<String, AlertEntry> mAlertEntries = new ArrayMap<>();
+
+ /**
+ * This is the list of entries that have already been removed from the
+ * NotificationManagerService side, but we keep it to prevent the UI from looking weird and
+ * will remove when possible. See {@link NotificationLifetimeExtender}
+ */
+ protected final ArraySet<NotificationData.Entry> mExtendedLifetimeAlertEntries =
+ new ArraySet<>();
+
+ protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
protected int mMinimumDisplayTime;
protected int mAutoDismissNotificationDecay;
@VisibleForTesting
@@ -74,7 +84,7 @@
if (alertEntry == null) {
return true;
}
- if (releaseImmediately || alertEntry.wasShownLongEnough()) {
+ if (releaseImmediately || canRemoveImmediately(key)) {
removeAlertEntry(key);
} else {
alertEntry.removeAsSoonAsPossible();
@@ -191,6 +201,12 @@
onAlertEntryRemoved(alertEntry);
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
alertEntry.reset();
+ if (mExtendedLifetimeAlertEntries.contains(entry)) {
+ if (mNotificationLifetimeFinishedCallback != null) {
+ mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
+ }
+ mExtendedLifetimeAlertEntries.remove(entry);
+ }
}
/**
@@ -207,6 +223,40 @@
return new AlertEntry();
}
+ /**
+ * Whether or not the alert can be removed currently. If it hasn't been on screen long enough
+ * it should not be removed unless forced
+ * @param key the key to check if removable
+ * @return true if the alert entry can be removed
+ */
+ protected boolean canRemoveImmediately(String key) {
+ AlertEntry alertEntry = mAlertEntries.get(key);
+ return alertEntry == null || alertEntry.wasShownLongEnough();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // NotificationLifetimeExtender Methods
+
+ @Override
+ public void setCallback(NotificationSafeToRemoveCallback callback) {
+ mNotificationLifetimeFinishedCallback = callback;
+ }
+
+ @Override
+ public boolean shouldExtendLifetime(NotificationData.Entry entry) {
+ return !canRemoveImmediately(entry.key);
+ }
+
+ @Override
+ public void setShouldManageLifetime(NotificationData.Entry entry, boolean shouldExtend) {
+ if (shouldExtend) {
+ mExtendedLifetimeAlertEntries.add(entry);
+ } else {
+ mExtendedLifetimeAlertEntries.remove(entry);
+ }
+ }
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
protected class AlertEntry implements Comparable<AlertEntry> {
@Nullable public NotificationData.Entry mEntry;
public long mPostTime;
@@ -214,11 +264,11 @@
@Nullable protected Runnable mRemoveAlertRunnable;
- public void setEntry(@Nullable final NotificationData.Entry entry) {
+ public void setEntry(@NonNull final NotificationData.Entry entry) {
setEntry(entry, () -> removeAlertEntry(entry.key));
}
- public void setEntry(@Nullable final NotificationData.Entry entry,
+ public void setEntry(@NonNull final NotificationData.Entry entry,
@Nullable Runnable removeAlertRunnable) {
mEntry = entry;
mRemoveAlertRunnable = removeAlertRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
new file mode 100644
index 0000000..62c21dc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
@@ -0,0 +1,53 @@
+package com.android.systemui.statusbar;
+
+import com.android.systemui.statusbar.notification.NotificationData;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Interface for anything that may need to keep notifications managed even after
+ * {@link NotificationListener} removes it. The lifetime extender is in charge of performing the
+ * callback when the notification is then safe to remove.
+ */
+public interface NotificationLifetimeExtender {
+
+ /**
+ * Set the handler to callback to when the notification is safe to remove.
+ *
+ * @param callback the handler to callback
+ */
+ void setCallback(@NonNull NotificationSafeToRemoveCallback callback);
+
+ /**
+ * Determines whether or not the extender needs the notification kept after removal.
+ *
+ * @param entry the entry containing the notification to check
+ * @return true if the notification lifetime should be extended
+ */
+ boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry);
+
+ /**
+ * Sets whether or not the lifetime should be managed by the extender. In practice, if
+ * shouldManage is true, this is where the extender starts managing the entry internally and is
+ * now responsible for calling {@link NotificationSafeToRemoveCallback#onSafeToRemove(String)}
+ * when the entry is safe to remove. If shouldManage is false, the extender no longer needs to
+ * worry about it (either because we will be removing it anyway or the entry is no longer
+ * removed due to an update).
+ *
+ * @param entry the entry that needs an extended lifetime
+ * @param shouldManage true if the extender should manage the entry now, false otherwise
+ */
+ void setShouldManageLifetime(@NonNull NotificationData.Entry entry, boolean shouldManage);
+
+ /**
+ * The callback for when the notification is now safe to remove (i.e. its lifetime has ended).
+ */
+ interface NotificationSafeToRemoveCallback {
+ /**
+ * Called when the lifetime extender determines it's safe to remove.
+ *
+ * @param key key of the entry that is now safe to remove
+ */
+ void onSafeToRemove(String key);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 9b375df..cfa09bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -76,7 +76,6 @@
mPresenter.getHandler().post(() -> {
processForRemoteInput(sbn.getNotification(), mContext);
String key = sbn.getKey();
- mEntryManager.removeKeyKeptForRemoteInput(key);
boolean isUpdate =
mEntryManager.getNotificationData().get(key) != null;
// In case we don't allow child notifications, we ignore children of
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 929713c..ea7e03e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -17,8 +17,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Context;
@@ -29,6 +31,7 @@
import android.os.SystemProperties;
import android.os.UserManager;
import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.view.MotionEvent;
@@ -50,6 +53,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Set;
/**
@@ -61,10 +65,10 @@
public class NotificationRemoteInputManager implements Dumpable {
public static final boolean ENABLE_REMOTE_INPUT =
SystemProperties.getBoolean("debug.enable_remote_input", true);
- public static final boolean FORCE_REMOTE_INPUT_HISTORY =
+ public static boolean FORCE_REMOTE_INPUT_HISTORY =
SystemProperties.getBoolean("debug.force_remoteinput_history", true);
private static final boolean DEBUG = false;
- private static final String TAG = "NotificationRemoteInputManager";
+ private static final String TAG = "NotifRemoteInputManager";
/**
* How long to wait before auto-dismissing a notification that was kept for remote input, and
@@ -74,12 +78,25 @@
*/
private static final int REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY = 200;
- protected final ArraySet<NotificationData.Entry> mRemoteInputEntriesToRemoveOnCollapse =
+ /**
+ * Notifications that are already removed but are kept around because we want to show the
+ * remote input history. See {@link RemoteInputHistoryExtender} and
+ * {@link SmartReplyHistoryExtender}.
+ */
+ protected final ArraySet<String> mKeysKeptForRemoteInputHistory = new ArraySet<>();
+
+ /**
+ * Notifications that are already removed but are kept around because the remote input is
+ * actively being used (i.e. user is typing in it). See {@link RemoteInputActiveExtender}.
+ */
+ protected final ArraySet<NotificationData.Entry> mEntriesKeptForRemoteInputActive =
new ArraySet<>();
// Dependencies:
protected final NotificationLockscreenUserManager mLockscreenUserManager =
Dependency.get(NotificationLockscreenUserManager.class);
+ protected final SmartReplyController mSmartReplyController =
+ Dependency.get(SmartReplyController.class);
protected final Context mContext;
private final UserManager mUserManager;
@@ -87,8 +104,11 @@
protected RemoteInputController mRemoteInputController;
protected NotificationPresenter mPresenter;
protected NotificationEntryManager mEntryManager;
+ protected NotificationLifetimeExtender.NotificationSafeToRemoveCallback
+ mNotificationLifetimeFinishedCallback;
protected IStatusBarService mBarService;
protected Callback mCallback;
+ protected final ArrayList<NotificationLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
@@ -276,6 +296,7 @@
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ addLifetimeExtenders();
}
public void setUpWithPresenter(NotificationPresenter presenter,
@@ -290,16 +311,16 @@
@Override
public void onRemoteInputSent(NotificationData.Entry entry) {
if (FORCE_REMOTE_INPUT_HISTORY
- && mEntryManager.isNotificationKeptForRemoteInput(entry.key)) {
- mEntryManager.removeNotification(entry.key, null);
- } else if (mRemoteInputEntriesToRemoveOnCollapse.contains(entry)) {
+ && isNotificationKeptForRemoteInputHistory(entry.key)) {
+ mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
+ } else if (mEntriesKeptForRemoteInputActive.contains(entry)) {
// We're currently holding onto this notification, but from the apps point of
// view it is already canceled, so we'll need to cancel it on the apps behalf
// after sending - unless the app posts an update in the mean time, so wait a
// bit.
mPresenter.getHandler().postDelayed(() -> {
- if (mRemoteInputEntriesToRemoveOnCollapse.remove(entry)) {
- mEntryManager.removeNotification(entry.key, null);
+ if (mEntriesKeptForRemoteInputActive.remove(entry)) {
+ mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
}
}, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
}
@@ -310,45 +331,74 @@
}
}
});
+ mSmartReplyController.setCallback((entry, reply) -> {
+ StatusBarNotification newSbn =
+ rebuildNotificationWithRemoteInput(entry, reply, true /* showSpinner */);
+ mEntryManager.updateNotification(newSbn, null /* ranking */);
+ });
+ }
+ /**
+ * Adds all the notification lifetime extenders. Each extender represents a reason for the
+ * NotificationRemoteInputManager to keep a notification lifetime extended.
+ */
+ protected void addLifetimeExtenders() {
+ mLifetimeExtenders.add(new RemoteInputHistoryExtender());
+ mLifetimeExtenders.add(new SmartReplyHistoryExtender());
+ mLifetimeExtenders.add(new RemoteInputActiveExtender());
+ }
+
+ public ArrayList<NotificationLifetimeExtender> getLifetimeExtenders() {
+ return mLifetimeExtenders;
}
public RemoteInputController getController() {
return mRemoteInputController;
}
- public void onUpdateNotification(NotificationData.Entry entry) {
- mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
- }
-
- /**
- * Returns true if NotificationRemoteInputManager wants to keep this notification around.
- *
- * @param entry notification being removed
- */
- public boolean onRemoveNotification(NotificationData.Entry entry) {
- if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
- && (entry.row != null && !entry.row.isDismissed())) {
- mRemoteInputEntriesToRemoveOnCollapse.add(entry);
- return true;
- }
- return false;
- }
-
public void onPerformRemoveNotification(StatusBarNotification n,
NotificationData.Entry entry) {
+ if (mKeysKeptForRemoteInputHistory.contains(n.getKey())) {
+ mKeysKeptForRemoteInputHistory.remove(n.getKey());
+ }
if (mRemoteInputController.isRemoteInputActive(entry)) {
mRemoteInputController.removeRemoteInput(entry, null);
}
}
- public void removeRemoteInputEntriesKeptUntilCollapsed() {
- for (int i = 0; i < mRemoteInputEntriesToRemoveOnCollapse.size(); i++) {
- NotificationData.Entry entry = mRemoteInputEntriesToRemoveOnCollapse.valueAt(i);
+ public void onPanelCollapsed() {
+ for (int i = 0; i < mEntriesKeptForRemoteInputActive.size(); i++) {
+ NotificationData.Entry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
mRemoteInputController.removeRemoteInput(entry, null);
- mEntryManager.removeNotification(entry.key, mEntryManager.getLatestRankingMap());
+ if (mNotificationLifetimeFinishedCallback != null) {
+ mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
+ }
}
- mRemoteInputEntriesToRemoveOnCollapse.clear();
+ mEntriesKeptForRemoteInputActive.clear();
+ }
+
+ public boolean isNotificationKeptForRemoteInputHistory(String key) {
+ return mKeysKeptForRemoteInputHistory.contains(key);
+ }
+
+ public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
+ if (entry.row == null || entry.row.isDismissed()) {
+ return false;
+ }
+ if (!FORCE_REMOTE_INPUT_HISTORY) {
+ return false;
+ }
+ return (mRemoteInputController.isSpinning(entry.key) || entry.hasJustSentRemoteInput());
+ }
+
+ public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
+ if (entry.row == null || entry.row.isDismissed()) {
+ return false;
+ }
+ if (!FORCE_REMOTE_INPUT_HISTORY) {
+ return false;
+ }
+ return mSmartReplyController.isSendingSmartReply(entry.key);
}
public void checkRemoteInputOutside(MotionEvent event) {
@@ -359,11 +409,63 @@
}
}
+ @VisibleForTesting
+ StatusBarNotification rebuildNotificationForCanceledSmartReplies(
+ NotificationData.Entry entry) {
+ return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
+ false /* showSpinner */);
+ }
+
+ @VisibleForTesting
+ StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry,
+ CharSequence remoteInputText, boolean showSpinner) {
+ StatusBarNotification sbn = entry.notification;
+
+ Notification.Builder b = Notification.Builder
+ .recoverBuilder(mContext, sbn.getNotification().clone());
+ if (remoteInputText != null) {
+ CharSequence[] oldHistory = sbn.getNotification().extras
+ .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ CharSequence[] newHistory;
+ if (oldHistory == null) {
+ newHistory = new CharSequence[1];
+ } else {
+ newHistory = new CharSequence[oldHistory.length + 1];
+ System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
+ }
+ newHistory[0] = String.valueOf(remoteInputText);
+ b.setRemoteInputHistory(newHistory);
+ }
+ b.setShowRemoteInputSpinner(showSpinner);
+ b.setHideSmartReplies(true);
+
+ Notification newNotification = b.build();
+
+ // Undo any compatibility view inflation
+ newNotification.contentView = sbn.getNotification().contentView;
+ newNotification.bigContentView = sbn.getNotification().bigContentView;
+ newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
+
+ return new StatusBarNotification(
+ sbn.getPackageName(),
+ sbn.getOpPkg(),
+ sbn.getId(),
+ sbn.getTag(),
+ sbn.getUid(),
+ sbn.getInitialPid(),
+ newNotification,
+ sbn.getUser(),
+ sbn.getOverrideGroupKey(),
+ sbn.getPostTime());
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NotificationRemoteInputManager state:");
- pw.print(" mRemoteInputEntriesToRemoveOnCollapse: ");
- pw.println(mRemoteInputEntriesToRemoveOnCollapse);
+ pw.print(" mKeysKeptForRemoteInputHistory: ");
+ pw.println(mKeysKeptForRemoteInputHistory);
+ pw.print(" mEntriesKeptForRemoteInputActive: ");
+ pw.println(mEntriesKeptForRemoteInputActive);
}
public void bindRow(ExpandableNotificationRow row) {
@@ -372,8 +474,133 @@
}
@VisibleForTesting
- public Set<NotificationData.Entry> getRemoteInputEntriesToRemoveOnCollapse() {
- return mRemoteInputEntriesToRemoveOnCollapse;
+ public Set<NotificationData.Entry> getEntriesKeptForRemoteInputActive() {
+ return mEntriesKeptForRemoteInputActive;
+ }
+
+ /**
+ * NotificationRemoteInputManager has multiple reasons to keep notification lifetime extended
+ * so we implement multiple NotificationLifetimeExtenders
+ */
+ protected abstract class RemoteInputExtender implements NotificationLifetimeExtender {
+ @Override
+ public void setCallback(NotificationSafeToRemoveCallback callback) {
+ if (mNotificationLifetimeFinishedCallback == null) {
+ mNotificationLifetimeFinishedCallback = callback;
+ }
+ }
+ }
+
+ /**
+ * Notification is kept alive as it was cancelled in response to a remote input interaction.
+ * This allows us to show what you replied and allows you to continue typing into it.
+ */
+ protected class RemoteInputHistoryExtender extends RemoteInputExtender {
+ @Override
+ public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+ return shouldKeepForRemoteInputHistory(entry);
+ }
+
+ @Override
+ public void setShouldManageLifetime(NotificationData.Entry entry,
+ boolean shouldExtend) {
+ if (shouldExtend) {
+ CharSequence remoteInputText = entry.remoteInputText;
+ if (TextUtils.isEmpty(remoteInputText)) {
+ remoteInputText = entry.remoteInputTextWhenReset;
+ }
+ StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry,
+ remoteInputText, false /* showSpinner */);
+ entry.onRemoteInputInserted();
+
+ if (newSbn == null) {
+ return;
+ }
+
+ mEntryManager.updateNotification(newSbn, null);
+
+ // Ensure the entry hasn't already been removed. This can happen if there is an
+ // inflation exception while updating the remote history
+ if (entry.row == null || entry.row.isRemoved()) {
+ return;
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Keeping notification around after sending remote input "
+ + entry.key);
+ }
+
+ mKeysKeptForRemoteInputHistory.add(entry.key);
+ } else {
+ mKeysKeptForRemoteInputHistory.remove(entry.key);
+ }
+ }
+ }
+
+ /**
+ * Notification is kept alive for smart reply history. Similar to REMOTE_INPUT_HISTORY but with
+ * {@link SmartReplyController} specific logic
+ */
+ protected class SmartReplyHistoryExtender extends RemoteInputExtender {
+ @Override
+ public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+ return shouldKeepForSmartReplyHistory(entry);
+ }
+
+ @Override
+ public void setShouldManageLifetime(NotificationData.Entry entry,
+ boolean shouldExtend) {
+ if (shouldExtend) {
+ StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry);
+
+ if (newSbn == null) {
+ return;
+ }
+
+ mEntryManager.updateNotification(newSbn, null);
+
+ if (entry.row == null || entry.row.isRemoved()) {
+ return;
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Keeping notification around after sending smart reply "
+ + entry.key);
+ }
+
+ mKeysKeptForRemoteInputHistory.add(entry.key);
+ } else {
+ mKeysKeptForRemoteInputHistory.remove(entry.key);
+ mSmartReplyController.stopSending(entry);
+ }
+ }
+ }
+
+ /**
+ * Notification is kept alive because the user is still using the remote input
+ */
+ protected class RemoteInputActiveExtender extends RemoteInputExtender {
+ @Override
+ public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+ if (entry.row == null || entry.row.isDismissed()) {
+ return false;
+ }
+ return mRemoteInputController.isRemoteInputActive(entry);
+ }
+
+ @Override
+ public void setShouldManageLifetime(NotificationData.Entry entry,
+ boolean shouldExtend) {
+ if (shouldExtend) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Keeping notification around while remote input active "
+ + entry.key);
+ }
+ mEntriesKeptForRemoteInputActive.add(entry);
+ } else {
+ mEntriesKeptForRemoteInputActive.remove(entry);
+ }
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index eaa4c6d..3bc4342 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -53,11 +53,8 @@
private static final String TAG = "ScrimView";
private final ColorExtractor.GradientColors mColors;
private int mDensity;
- private boolean mDrawAsSrc;
private float mViewAlpha = 1.0f;
private ValueAnimator mAlphaAnimator;
- private Rect mExcludedRect = new Rect();
- private boolean mHasExcludedArea;
private Drawable mDrawable;
private PorterDuffColorFilter mColorFilter;
private int mTintColor;
@@ -137,59 +134,8 @@
@Override
protected void onDraw(Canvas canvas) {
- if (mDrawAsSrc || mDrawable.getAlpha() > 0) {
- if (!mHasExcludedArea) {
- mDrawable.draw(canvas);
- } else {
- if (mExcludedRect.top > 0) {
- canvas.save();
- canvas.clipRect(0, 0, getWidth(), mExcludedRect.top);
- mDrawable.draw(canvas);
- canvas.restore();
- }
- if (mExcludedRect.left > 0) {
- canvas.save();
- canvas.clipRect(0, mExcludedRect.top, mExcludedRect.left,
- mExcludedRect.bottom);
- mDrawable.draw(canvas);
- canvas.restore();
- }
- if (mExcludedRect.right < getWidth()) {
- canvas.save();
- canvas.clipRect(mExcludedRect.right, mExcludedRect.top, getWidth(),
- mExcludedRect.bottom);
- mDrawable.draw(canvas);
- canvas.restore();
- }
- if (mExcludedRect.bottom < getHeight()) {
- canvas.save();
- canvas.clipRect(0, mExcludedRect.bottom, getWidth(), getHeight());
- mDrawable.draw(canvas);
- canvas.restore();
- }
- // We also need to draw the rounded corners of the background
- canvas.save();
- canvas.clipRect(mExcludedRect.left, mExcludedRect.top,
- mExcludedRect.left + mCornerRadius, mExcludedRect.top + mCornerRadius);
- mDrawable.draw(canvas);
- canvas.restore();
- canvas.save();
- canvas.clipRect(mExcludedRect.right - mCornerRadius, mExcludedRect.top,
- mExcludedRect.right, mExcludedRect.top + mCornerRadius);
- mDrawable.draw(canvas);
- canvas.restore();
- canvas.save();
- canvas.clipRect(mExcludedRect.left, mExcludedRect.bottom - mCornerRadius,
- mExcludedRect.left + mCornerRadius, mExcludedRect.bottom);
- mDrawable.draw(canvas);
- canvas.restore();
- canvas.save();
- canvas.clipRect(mExcludedRect.right - mCornerRadius,
- mExcludedRect.bottom - mCornerRadius,
- mExcludedRect.right, mExcludedRect.bottom);
- mDrawable.draw(canvas);
- canvas.restore();
- }
+ if (mDrawable.getAlpha() > 0) {
+ mDrawable.draw(canvas);
}
}
@@ -198,7 +144,6 @@
mDrawable.setCallback(this);
mDrawable.setBounds(getLeft(), getTop(), getRight(), getBottom());
mDrawable.setAlpha((int) (255 * mViewAlpha));
- setDrawAsSrc(mDrawAsSrc);
updateScreenSize();
invalidate();
}
@@ -211,12 +156,6 @@
}
}
- public void setDrawAsSrc(boolean asSrc) {
- mDrawAsSrc = asSrc;
- PorterDuff.Mode mode = asSrc ? PorterDuff.Mode.SRC : PorterDuff.Mode.SRC_OVER;
- mDrawable.setXfermode(new PorterDuffXfermode(mode));
- }
-
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -328,22 +267,6 @@
return mViewAlpha;
}
- public void setExcludedArea(Rect area) {
- if (area == null) {
- mHasExcludedArea = false;
- invalidate();
- return;
- }
-
- int left = Math.max(area.left, 0);
- int top = Math.max(area.top, 0);
- int right = Math.min(area.right, getWidth());
- int bottom = Math.min(area.bottom, getHeight());
- mExcludedRect.set(left, top, right, bottom);
- mHasExcludedArea = left < right && top < bottom;
- invalidate();
- }
-
public void setChangeRunnable(Runnable changeRunnable) {
mChangeRunnable = changeRunnable;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index e43c9e5..fb888dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -33,20 +33,19 @@
public class SmartReplyController {
private IStatusBarService mBarService;
private Set<String> mSendingKeys = new ArraySet<>();
+ private Callback mCallback;
public SmartReplyController() {
mBarService = Dependency.get(IStatusBarService.class);
}
- public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply) {
- NotificationEntryManager notificationEntryManager
- = Dependency.get(NotificationEntryManager.class);
- StatusBarNotification newSbn =
- notificationEntryManager.rebuildNotificationWithRemoteInput(entry, reply,
- true /* showSpinner */);
- notificationEntryManager.updateNotification(newSbn, null /* ranking */);
- mSendingKeys.add(entry.key);
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+ public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply) {
+ mCallback.onSmartReplySent(entry, reply);
+ mSendingKeys.add(entry.key);
try {
mBarService.onNotificationSmartReplySent(entry.notification.getKey(),
replyIndex);
@@ -77,4 +76,17 @@
mSendingKeys.remove(entry.notification.getKey());
}
}
+
+ /**
+ * Callback for any class that needs to do something in response to a smart reply being sent.
+ */
+ public interface Callback {
+ /**
+ * A smart reply has just been sent for a notification
+ *
+ * @param entry the entry for the notification
+ * @param reply the reply that was sent
+ */
+ void onSmartReplySent(NotificationData.Entry entry, CharSequence reply);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index ed06752..304a00f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -495,32 +495,37 @@
}
@Override
- public void onUserSwitched(int newUserId) {
- super.onUserSwitched(newUserId);
- if (mFullscreenUserSwitcher != null) {
- mFullscreenUserSwitcher.onUserSwitched(newUserId);
- }
- }
-
- @Override
public void onStateChanged(int newState) {
super.onStateChanged(newState);
- CarUserManagerHelper helper = new CarUserManagerHelper(mContext);
- if (!helper.isHeadlessSystemUser()) {
- showUserSwitcher();
+ if (newState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
+ if (!mFullscreenUserSwitcher.isVisible()) {
+ // Current execution path continues to set state after this, thus we deffer the
+ // dismissal to the next execution cycle.
+ postDismissKeyguard(); // Dismiss the keyguard if switcher is not visible.
+ }
+ } else {
+ mFullscreenUserSwitcher.hide();
}
}
public void showUserSwitcher() {
- if (mFullscreenUserSwitcher != null) {
- if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
- mFullscreenUserSwitcher.show();
- } else {
- mFullscreenUserSwitcher.hide();
- }
+ if (mFullscreenUserSwitcher != null && mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
+ mFullscreenUserSwitcher.show(); // Makes the switcher visible.
}
}
+ public void postDismissKeyguard() {
+ mHandler.post(this::dismissKeyguard);
+ }
+
+ /**
+ * Dismisses the keyguard and shows bouncer if authentication is necessary.
+ */
+ public void dismissKeyguard() {
+ executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
+ true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+ }
+
@Override
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
// Do nothing, we don't want to display media art in the lock screen for a car.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 67a76fd..2ebf5eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,7 +18,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.car.user.CarUserManagerHelper;
import android.content.Context;
import android.view.View;
import android.view.ViewStub;
@@ -26,115 +25,87 @@
import androidx.recyclerview.widget.GridLayoutManager;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.StatusBar;
/**
* Manages the fullscreen user switcher.
*/
public class FullscreenUserSwitcher {
- private final View mContainer;
- private final View mParent;
private final UserGridRecyclerView mUserGridView;
+ private final View mParent;
private final int mShortAnimDuration;
- private final StatusBar mStatusBar;
- private final CarUserManagerHelper mCarUserManagerHelper;
- private boolean mShowing;
+ private final CarStatusBar mStatusBar;
- public FullscreenUserSwitcher(StatusBar statusBar, ViewStub containerStub, Context context) {
+ public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
mStatusBar = statusBar;
mParent = containerStub.inflate();
- mContainer = mParent.findViewById(R.id.container);
- mUserGridView = mContainer.findViewById(R.id.user_grid);
+ mParent.setVisibility(View.VISIBLE);
+ View container = mParent.findViewById(R.id.container);
+
+ // Initialize user grid.
+ mUserGridView = container.findViewById(R.id.user_grid);
GridLayoutManager layoutManager = new GridLayoutManager(context,
- context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
+ context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
mUserGridView.getRecyclerView().setLayoutManager(layoutManager);
mUserGridView.buildAdapter();
mUserGridView.setUserSelectionListener(this::onUserSelected);
- mCarUserManagerHelper = new CarUserManagerHelper(context);
+ // Hide the user grid by default. It will only be made visible by clicking on a cancel
+ // button in a bouncer.
+ hide();
- mShortAnimDuration = mContainer.getResources()
+ mShortAnimDuration = container.getResources()
.getInteger(android.R.integer.config_shortAnimTime);
}
+ /**
+ * Makes user grid visible.
+ */
public void show() {
- if (mCarUserManagerHelper.isHeadlessSystemUser()) {
- showUserGrid();
- }
- if (mShowing) {
- return;
- }
- mShowing = true;
- mParent.setVisibility(View.VISIBLE);
- }
-
- public void hide() {
- mShowing = false;
- mParent.setVisibility(View.GONE);
- }
-
- public void onUserSwitched(int newUserId) {
- toggleSwitchInProgress(false);
- mParent.post(this::dismissKeyguard);
- }
-
- private void onUserSelected(UserGridRecyclerView.UserRecord record) {
- if (mCarUserManagerHelper.isHeadlessSystemUser()) {
- hideUserGrid();
- }
-
- if (record.mIsForeground || (record.mIsStartGuestSession
- && mCarUserManagerHelper.isForegroundUserGuest())) {
- dismissKeyguard();
- return;
- }
- toggleSwitchInProgress(true);
- }
-
- private void showUserGrid() {
mUserGridView.setVisibility(View.VISIBLE);
}
- private void hideUserGrid() {
+ /**
+ * Hides the user grid.
+ */
+ public void hide() {
mUserGridView.setVisibility(View.INVISIBLE);
}
- // Dismisses the keyguard and shows bouncer if authentication is necessary.
- private void dismissKeyguard() {
- mStatusBar.executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
- true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+ /**
+ * @return {@code true} if user grid is visible, {@code false} otherwise.
+ */
+ public boolean isVisible() {
+ return mUserGridView.getVisibility() == View.VISIBLE;
}
- private void toggleSwitchInProgress(boolean inProgress) {
- if (inProgress) {
- fadeOut(mContainer);
- } else {
- fadeIn(mContainer);
+ /**
+ * Every time user clicks on an item in the switcher, we hide the switcher, either
+ * gradually or immediately.
+ *
+ * We dismiss the entire keyguard if user clicked on the foreground user (user we're already
+ * logged in as).
+ */
+ private void onUserSelected(UserGridRecyclerView.UserRecord record) {
+ if (record.mIsForeground) {
+ hide();
+ mStatusBar.dismissKeyguard();
+ return;
}
+ // Switching is about to happen, since it takes time, fade out the switcher gradually.
+ fadeOut();
}
- private void fadeOut(View view) {
- view.animate()
+ private void fadeOut() {
+ mUserGridView.animate()
.alpha(0.0f)
.setDuration(mShortAnimDuration)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- view.setVisibility(View.GONE);
+ hide();
+ mUserGridView.setAlpha(1.0f);
}
});
- }
- private void fadeIn(View view) {
- view.animate()
- .alpha(1.0f)
- .setDuration(mShortAnimDuration)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- view.setAlpha(0.0f);
- view.setVisibility(View.VISIBLE);
- }
- });
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index b655a6b..ac01fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -37,7 +37,6 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
@@ -58,6 +57,7 @@
import com.android.systemui.R;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -65,7 +65,6 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -100,8 +99,6 @@
protected final Context mContext;
protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
protected final NotificationClicker mNotificationClicker = new NotificationClicker();
- protected final ArraySet<NotificationData.Entry> mHeadsUpEntriesToRemoveOnSwitch =
- new ArraySet<>();
// Dependencies:
protected final NotificationLockscreenUserManager mLockscreenUserManager =
@@ -124,8 +121,6 @@
Dependency.get(ForegroundServiceController.class);
protected final NotificationListener mNotificationListener =
Dependency.get(NotificationListener.class);
- private final SmartReplyController mSmartReplyController =
- Dependency.get(SmartReplyController.class);
protected IStatusBarService mBarService;
protected NotificationPresenter mPresenter;
@@ -139,13 +134,9 @@
protected boolean mUseHeadsUp = false;
protected boolean mDisableNotificationAlerts;
protected NotificationListContainer mListContainer;
+ protected final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
+ = new ArrayList<>();
private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
- /**
- * Notifications with keys in this set are not actually around anymore. We kept them around
- * when they were canceled in response to a remote input interaction. This allows us to show
- * what you replied and allows you to continue typing into it.
- */
- private final ArraySet<String> mKeysKeptForRemoteInput = new ArraySet<>();
private final class NotificationClicker implements View.OnClickListener {
@@ -198,14 +189,6 @@
}
};
- public NotificationListenerService.RankingMap getLatestRankingMap() {
- return mLatestRankingMap;
- }
-
- public void setLatestRankingMap(NotificationListenerService.RankingMap latestRankingMap) {
- mLatestRankingMap = latestRankingMap;
- }
-
public void setDisableNotificationAlerts(boolean disableNotificationAlerts) {
mDisableNotificationAlerts = disableNotificationAlerts;
mHeadsUpObserver.onChange(true);
@@ -215,18 +198,6 @@
mDeviceProvisionedController.removeCallback(mDeviceProvisionedListener);
}
- public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
- if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
- removeNotification(entry.key, getLatestRankingMap());
- mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
- if (mHeadsUpEntriesToRemoveOnSwitch.isEmpty()) {
- setLatestRankingMap(null);
- }
- } else {
- updateNotificationRanking(null);
- }
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NotificationEntryManager state:");
@@ -240,8 +211,6 @@
}
pw.print(" mUseHeadsUp=");
pw.println(mUseHeadsUp);
- pw.print(" mKeysKeptForRemoteInput: ");
- pw.println(mKeysKeptForRemoteInput);
}
public NotificationEntryManager(Context context) {
@@ -294,6 +263,14 @@
mHeadsUpObserver);
}
+ mNotificationLifetimeExtenders.add(mHeadsUpManager);
+ mNotificationLifetimeExtenders.add(mGutsManager);
+ mNotificationLifetimeExtenders.addAll(mRemoteInputManager.getLifetimeExtenders());
+
+ for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+ extender.setCallback(key -> removeNotification(key, mLatestRankingMap));
+ }
+
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
mHeadsUpObserver.onChange(true); // set up
@@ -397,11 +374,6 @@
true);
NotificationData.Entry entry = mNotificationData.get(n.getKey());
- if (FORCE_REMOTE_INPUT_HISTORY
- && mKeysKeptForRemoteInput.contains(n.getKey())) {
- mKeysKeptForRemoteInput.remove(n.getKey());
- }
-
mRemoteInputManager.onPerformRemoveNotification(n, entry);
final String pkg = n.getPackageName();
final String tag = n.getTag();
@@ -433,7 +405,7 @@
* WARNING: this will call back into us. Don't hold any locks.
*/
void handleNotificationError(StatusBarNotification n, String message) {
- removeNotification(n.getKey(), null);
+ removeNotificationInternal(n.getKey(), null, true /* forceRemove */);
try {
mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
n.getInitialPid(), message, n.getUserId());
@@ -487,7 +459,11 @@
@Override
public void removeNotification(String key, NotificationListenerService.RankingMap ranking) {
- boolean deferRemoval = false;
+ removeNotificationInternal(key, ranking, false /* forceRemove */);
+ }
+
+ private void removeNotificationInternal(String key,
+ @Nullable NotificationListenerService.RankingMap ranking, boolean forceRemove) {
abortExistingInflation(key);
if (mHeadsUpManager.contains(key)) {
// A cancel() in response to a remote input shouldn't be delayed, as it makes the
@@ -497,154 +473,53 @@
boolean ignoreEarliestRemovalTime = mRemoteInputManager.getController().isSpinning(key)
&& !FORCE_REMOTE_INPUT_HISTORY
|| !mVisualStabilityManager.isReorderingAllowed();
- deferRemoval = !mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
+
+ // Attempt to remove notification.
+ mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
}
- mMediaManager.onNotificationRemoved(key);
NotificationData.Entry entry = mNotificationData.get(key);
- if (FORCE_REMOTE_INPUT_HISTORY
- && shouldKeepForRemoteInput(entry)
- && entry.row != null && !entry.row.isDismissed()) {
- CharSequence remoteInputText = entry.remoteInputText;
- if (TextUtils.isEmpty(remoteInputText)) {
- remoteInputText = entry.remoteInputTextWhenReset;
- }
- StatusBarNotification newSbn = rebuildNotificationWithRemoteInput(entry,
- remoteInputText, false /* showSpinner */);
- boolean updated = false;
- entry.onRemoteInputInserted();
- try {
- updateNotificationInternal(newSbn, null);
- updated = true;
- } catch (InflationException e) {
- deferRemoval = false;
- }
- if (updated) {
- Log.w(TAG, "Keeping notification around after sending remote input "+ entry.key);
- addKeyKeptForRemoteInput(entry.key);
- return;
- }
- }
- if (FORCE_REMOTE_INPUT_HISTORY
- && shouldKeepForSmartReply(entry)
- && entry.row != null && !entry.row.isDismissed()) {
- // Turn off the spinner and hide buttons when an app cancels the notification.
- StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry);
- boolean updated = false;
- try {
- updateNotificationInternal(newSbn, null);
- updated = true;
- } catch (InflationException e) {
- // Ignore just don't keep the notification around.
- }
- // Treat the reply as longer sending.
- mSmartReplyController.stopSending(entry);
- if (updated) {
- Log.w(TAG, "Keeping notification around after sending smart reply " + entry.key);
- addKeyKeptForRemoteInput(entry.key);
- return;
- }
- }
-
- // Actually removing notification so smart reply controller can forget about it.
- mSmartReplyController.stopSending(entry);
-
- if (deferRemoval) {
- mLatestRankingMap = ranking;
- mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
+ if (entry == null) {
+ mCallback.onNotificationRemoved(key, null /* old */);
return;
}
- if (mRemoteInputManager.onRemoveNotification(entry)) {
- mLatestRankingMap = ranking;
- return;
+ // If a manager needs to keep the notification around for whatever reason, we return early
+ // and keep the notification
+ if (!forceRemove) {
+ for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+ if (extender.shouldExtendLifetime(entry)) {
+ mLatestRankingMap = ranking;
+ extender.setShouldManageLifetime(entry, true /* shouldManage */);
+ return;
+ }
+ }
}
- if (entry != null && mGutsManager.getExposedGuts() != null
- && mGutsManager.getExposedGuts() == entry.row.getGuts()
- && entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
- Log.w(TAG, "Keeping notification because it's showing guts. " + key);
- mLatestRankingMap = ranking;
- mGutsManager.setKeyToRemoveOnGutsClosed(key);
- return;
+ // At this point, we are guaranteed the notification will be removed
+
+ // Ensure any managers keeping the lifetime extended stop managing the entry
+ for (NotificationLifetimeExtender extender: mNotificationLifetimeExtenders) {
+ extender.setShouldManageLifetime(entry, false /* shouldManage */);
}
- if (entry != null) {
- mForegroundServiceController.removeNotification(entry.notification);
- }
+ mMediaManager.onNotificationRemoved(key);
+ mForegroundServiceController.removeNotification(entry.notification);
- if (entry != null && entry.row != null) {
+ if (entry.row != null) {
entry.row.setRemoved();
mListContainer.cleanUpViewState(entry.row);
}
+
// Let's remove the children if this was a summary
handleGroupSummaryRemoved(key);
+
StatusBarNotification old = removeNotificationViews(key, ranking);
mCallback.onNotificationRemoved(key, old);
}
- public StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry,
- CharSequence remoteInputText, boolean showSpinner) {
- StatusBarNotification sbn = entry.notification;
-
- Notification.Builder b = Notification.Builder
- .recoverBuilder(mContext, sbn.getNotification().clone());
- if (remoteInputText != null) {
- CharSequence[] oldHistory = sbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
- CharSequence[] newHistory;
- if (oldHistory == null) {
- newHistory = new CharSequence[1];
- } else {
- newHistory = new CharSequence[oldHistory.length + 1];
- System.arraycopy(oldHistory, 0, newHistory, 1, oldHistory.length);
- }
- newHistory[0] = String.valueOf(remoteInputText);
- b.setRemoteInputHistory(newHistory);
- }
- b.setShowRemoteInputSpinner(showSpinner);
- b.setHideSmartReplies(true);
-
- Notification newNotification = b.build();
-
- // Undo any compatibility view inflation
- newNotification.contentView = sbn.getNotification().contentView;
- newNotification.bigContentView = sbn.getNotification().bigContentView;
- newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
-
- StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
- sbn.getOpPkg(),
- sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
- newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
- return newSbn;
- }
-
- @VisibleForTesting
- StatusBarNotification rebuildNotificationForCanceledSmartReplies(
- NotificationData.Entry entry) {
- return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
- false /* showSpinner */);
- }
-
- private boolean shouldKeepForSmartReply(NotificationData.Entry entry) {
- return entry != null && mSmartReplyController.isSendingSmartReply(entry.key);
- }
-
- private boolean shouldKeepForRemoteInput(NotificationData.Entry entry) {
- if (entry == null) {
- return false;
- }
- if (mRemoteInputManager.getController().isSpinning(entry.key)) {
- return true;
- }
- if (entry.hasJustSentRemoteInput()) {
- return true;
- }
- return false;
- }
-
private StatusBarNotification removeNotificationViews(String key,
NotificationListenerService.RankingMap ranking) {
NotificationData.Entry entry = mNotificationData.remove(key, ranking);
@@ -683,9 +558,9 @@
NotificationData.Entry childEntry = row.getEntry();
boolean isForeground = (row.getStatusBarNotification().getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0;
- boolean keepForReply = FORCE_REMOTE_INPUT_HISTORY
- && (shouldKeepForRemoteInput(childEntry)
- || shouldKeepForSmartReply(childEntry));
+ boolean keepForReply =
+ mRemoteInputManager.shouldKeepForRemoteInputHistory(childEntry)
+ || mRemoteInputManager.shouldKeepForSmartReplyHistory(childEntry);
if (isForeground || keepForReply) {
// the child is a foreground service notification which we can't remove or it's
// a child we're keeping around for reply!
@@ -868,13 +743,11 @@
if (entry == null) {
return;
}
- mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
- mRemoteInputManager.onUpdateNotification(entry);
- mSmartReplyController.stopSending(entry);
- if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
- mGutsManager.setKeyToRemoveOnGutsClosed(null);
- Log.w(TAG, "Notification that was kept for guts was updated. " + key);
+ // Notification is updated so it is essentially re-added and thus alive again. Don't need
+ // to keep its lifetime extended.
+ for (NotificationLifetimeExtender extender : mNotificationLifetimeExtenders) {
+ extender.setShouldManageLifetime(entry, false /* shouldManage */);
}
Notification n = notification.getNotification();
@@ -1080,20 +953,6 @@
return mHeadsUpManager.contains(key);
}
- public boolean isNotificationKeptForRemoteInput(String key) {
- return mKeysKeptForRemoteInput.contains(key);
- }
-
- public void removeKeyKeptForRemoteInput(String key) {
- mKeysKeptForRemoteInput.remove(key);
- }
-
- public void addKeyKeptForRemoteInput(String key) {
- if (FORCE_REMOTE_INPUT_HISTORY) {
- mKeysKeptForRemoteInput.add(key);
- }
- }
-
/**
* Callback for NotificationEntryManager.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
new file mode 100644
index 0000000..f204c42
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ShadeViewRefactor.java
@@ -0,0 +1,36 @@
+/*
+ * 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 java.lang.annotation;
+
+@Retention(RetentionPolicy.SOURCE)
+public @interface ShadeViewRefactor {
+ /**
+ * Returns the refactor component.
+ * @return the refactor component.
+ */
+ RefactorComponent value();
+
+ public enum RefactorComponent {
+ ADAPTER,
+ LAYOUT_ALGORITHM,
+ STATE_RESOLVER,
+ DECORATOR,
+ INPUT,
+ COORDINATOR,
+ SHADE_VIEW
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index e635976..f4ef0f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -38,27 +38,27 @@
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationLifetimeExtender;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationPresenter;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import androidx.annotation.VisibleForTesting;
-
/**
* Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
* closing guts, and keeping track of the currently exposed notification guts.
*/
-public class NotificationGutsManager implements Dumpable {
+public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
private static final String TAG = "NotificationGutsManager";
// Must match constant in Settings. Used to highlight preferences when linking to Settings.
@@ -75,12 +75,13 @@
// which notification is currently being longpress-examined by the user
private NotificationGuts mNotificationGutsExposed;
private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
- protected NotificationPresenter mPresenter;
- protected NotificationEntryManager mEntryManager;
+ private NotificationPresenter mPresenter;
+ private NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
private NotificationListContainer mListContainer;
private NotificationInfo.CheckSaveListener mCheckSaveListener;
private OnSettingsClickListener mOnSettingsClickListener;
- private String mKeyToRemoveOnGutsClosed;
+ @VisibleForTesting
+ protected String mKeyToRemoveOnGutsClosed;
public NotificationGutsManager(Context context) {
mContext = context;
@@ -91,24 +92,15 @@
}
public void setUpWithPresenter(NotificationPresenter presenter,
- NotificationEntryManager entryManager, NotificationListContainer listContainer,
+ NotificationListContainer listContainer,
NotificationInfo.CheckSaveListener checkSaveListener,
OnSettingsClickListener onSettingsClickListener) {
mPresenter = presenter;
- mEntryManager = entryManager;
mListContainer = listContainer;
mCheckSaveListener = checkSaveListener;
mOnSettingsClickListener = onSettingsClickListener;
}
- public String getKeyToRemoveOnGutsClosed() {
- return mKeyToRemoveOnGutsClosed;
- }
-
- public void setKeyToRemoveOnGutsClosed(String keyToRemoveOnGutsClosed) {
- mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
- }
-
public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
setExposedGuts(row.getGuts());
bindGuts(row);
@@ -171,7 +163,9 @@
String key = sbn.getKey();
if (key.equals(mKeyToRemoveOnGutsClosed)) {
mKeyToRemoveOnGutsClosed = null;
- mEntryManager.removeNotification(key, mEntryManager.getLatestRankingMap());
+ if (mNotificationLifetimeFinishedCallback != null) {
+ mNotificationLifetimeFinishedCallback.onSafeToRemove(key);
+ }
}
});
@@ -410,6 +404,37 @@
}
@Override
+ public void setCallback(NotificationSafeToRemoveCallback callback) {
+ mNotificationLifetimeFinishedCallback = callback;
+ }
+
+ @Override
+ public boolean shouldExtendLifetime(NotificationData.Entry entry) {
+ return entry != null
+ &&(mNotificationGutsExposed != null
+ && entry.row.getGuts() != null
+ && mNotificationGutsExposed == entry.row.getGuts()
+ && !mNotificationGutsExposed.isLeavebehind());
+ }
+
+ @Override
+ public void setShouldManageLifetime(NotificationData.Entry entry, boolean shouldExtend) {
+ if (shouldExtend) {
+ mKeyToRemoveOnGutsClosed = entry.key;
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Keeping notification because it's showing guts. " + entry.key);
+ }
+ } else {
+ if (mKeyToRemoveOnGutsClosed != null && mKeyToRemoveOnGutsClosed.equals(entry.key)) {
+ mKeyToRemoveOnGutsClosed = null;
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Notification that was kept for guts was updated. " + entry.key);
+ }
+ }
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NotificationGutsManager state:");
pw.print(" mKeyToRemoveOnGutsClosed: ");
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 4f554b6..52844fe 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
@@ -134,6 +134,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.ShadeViewRefactor;
+import java.lang.annotation.ShadeViewRefactor.RefactorComponent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -394,9 +396,6 @@
};
private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
private boolean mPulsing;
- private boolean mDrawBackgroundAsSrc;
- private boolean mFadingOut;
- private boolean mParentNotFullyVisible;
private boolean mGroupExpandedForMeasure;
private boolean mScrollable;
private View mForcedScroll;
@@ -468,18 +467,22 @@
private Interpolator mDarkXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
private NotificationPanelView mNotificationPanel;
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationStackScrollLayout(Context context) {
this(context, null);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationStackScrollLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationStackScrollLayout(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
@@ -527,6 +530,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onFinishInflate() {
super.onFinishInflate();
@@ -537,6 +541,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onDensityOrFontScaleChanged() {
inflateFooterView();
inflateEmptyShadeView();
@@ -544,6 +549,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onThemeChanged() {
int which;
if (mStatusBarState == StatusBarState.KEYGUARD
@@ -560,6 +566,7 @@
}
@VisibleForTesting
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateFooter() {
boolean showDismissView = mClearAllEnabled && hasActiveClearableNotifications();
boolean showFooterView = (showDismissView ||
@@ -573,6 +580,7 @@
/**
* Return whether there are any clearable notifications
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean hasActiveClearableNotifications() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -587,7 +595,8 @@
return false;
}
- public RemoteInputController.Delegate createDelegate() {
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationData.Entry entry,
boolean remoteInputActive) {
@@ -608,6 +617,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Dependency.get(StatusBarStateController.class).addListener(mStateListener);
@@ -615,6 +625,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Dependency.get(StatusBarStateController.class).removeListener(mStateListener);
@@ -622,11 +633,13 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public NotificationSwipeActionHelper getSwipeActionHelper() {
return mSwipeHelper;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuClicked(View view, int x, int y, MenuItem item) {
if (mLongPressListener == null) {
return;
@@ -640,6 +653,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuReset(View row) {
if (mTranslatingParentView != null && row == mTranslatingParentView) {
mMenuExposedView = null;
@@ -648,6 +662,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void onMenuShown(View row) {
mMenuExposedView = mTranslatingParentView;
if (row instanceof ExpandableNotificationRow) {
@@ -659,6 +674,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onUiModeChanged() {
mBgColor = mContext.getColor(R.color.notification_shade_background_color);
updateBackgroundDimming();
@@ -673,6 +689,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.DECORATOR)
protected void onDraw(Canvas canvas) {
if (mShouldDrawNotificationBackground
&& (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
@@ -689,6 +706,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.DECORATOR)
private void drawBackground(Canvas canvas) {
final int lockScreenLeft = mSidePaddings;
final int lockScreenRight = getWidth() - mSidePaddings;
@@ -732,6 +750,7 @@
updateClipping();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateBackgroundDimming() {
// No need to update the background color if it's not being drawn.
if (!mShouldDrawNotificationBackground) {
@@ -758,6 +777,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void initView(Context context) {
mScroller = new OverScroller(getContext());
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
@@ -789,25 +809,12 @@
R.dimen.heads_up_status_bar_padding);
}
- public void setDrawBackgroundAsSrc(boolean asSrc) {
- mDrawBackgroundAsSrc = asSrc;
- updateSrcDrawing();
- }
-
- private void updateSrcDrawing() {
- if (!mShouldDrawNotificationBackground) {
- return;
- }
-
- mBackgroundPaint.setXfermode(mDrawBackgroundAsSrc && !mFadingOut && !mParentNotFullyVisible
- ? mSrcMode : null);
- invalidate();
- }
-
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void notifyHeightChangeListener(ExpandableView view) {
notifyHeightChangeListener(view, false /* needsAnimation */);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void notifyHeightChangeListener(ExpandableView view, boolean needsAnimation) {
if (mOnHeightChangedListener != null) {
mOnHeightChangedListener.onHeightChanged(view, needsAnimation);
@@ -815,6 +822,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -834,6 +842,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// we layout all our children centered on the top
float centerX = getWidth() / 2.0f;
@@ -856,6 +865,7 @@
updateAlgorithmLayoutMinHeight();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
if (mAnimationsEnabled && (mIsExpanded || row != null && row.isPinned())) {
mNeedViewResizeAnimation = true;
@@ -863,18 +873,21 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
public void updateSpeedBumpIndex(int newIndex, boolean noAmbient) {
mAmbientState.setSpeedBumpIndex(newIndex);
mNoAmbient = noAmbient;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setChildLocationsChangedListener(
NotificationLogger.OnChildLocationsChangedListener listener) {
mListener = listener;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
public boolean isInVisibleLocation(ExpandableNotificationRow row) {
ExpandableViewState childViewState = mCurrentStackScrollState.getViewStateForView(row);
if (childViewState == null) {
@@ -889,12 +902,14 @@
return true;
}
+ @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void setMaxLayoutHeight(int maxLayoutHeight) {
mMaxLayoutHeight = maxLayoutHeight;
mShelf.setMaxLayoutHeight(maxLayoutHeight);
updateAlgorithmHeightAndPadding();
}
+ @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void updateAlgorithmHeightAndPadding() {
mTopPadding = (int) MathUtils.lerp(mRegularTopPadding, mDarkTopPadding,
mInterpolatedDarkAmount);
@@ -903,6 +918,7 @@
mAmbientState.setTopPadding(mTopPadding);
}
+ @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
private void updateAlgorithmLayoutMinHeight() {
mAmbientState.setLayoutMinHeight(mQsExpanded || isHeadsUpTransition()
? getLayoutMinHeight() : 0);
@@ -912,6 +928,7 @@
* Updates the children views according to the stack scroll algorithm. Call this whenever
* modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
*/
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateChildren() {
updateScrollStateForAddedChildren();
mAmbientState.setCurrentScrollVelocity(mScroller.isFinished()
@@ -926,6 +943,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void onPreDrawDuringAnimation() {
mShelf.updateAppearance();
updateClippingToTopRoundedCorner();
@@ -934,6 +952,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateClippingToTopRoundedCorner() {
Float clipStart = (float) mTopPadding
+ mStackTranslation
@@ -956,6 +975,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateScrollStateForAddedChildren() {
if (mChildrenToAddAnimated.isEmpty()) {
return;
@@ -979,6 +999,7 @@
clampScrollPosition();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateForcedScroll() {
if (mForcedScroll != null && (!mForcedScroll.hasFocus()
|| !mForcedScroll.isAttachedToWindow())) {
@@ -1000,6 +1021,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void requestChildrenUpdate() {
if (!mChildrenUpdateRequested) {
getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
@@ -1008,10 +1030,12 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean isCurrentlyAnimating() {
return mStateAnimator.isRunning();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void clampScrollPosition() {
int scrollRange = getScrollRange();
if (scrollRange < mOwnScrollY) {
@@ -1019,10 +1043,12 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public int getTopPadding() {
return mTopPadding;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void setTopPadding(int topPadding, boolean animate) {
if (mRegularTopPadding != topPadding) {
mRegularTopPadding = topPadding;
@@ -1044,6 +1070,7 @@
*
* @param height the expanded height of the panel
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setExpandedHeight(float height) {
mExpandedHeight = height;
setIsExpanded(height > 0);
@@ -1110,6 +1137,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setRequestedClipBounds(Rect clipRect) {
mRequestedClipBounds = clipRect;
updateClipping();
@@ -1118,17 +1146,18 @@
/**
* Return the height of the content ignoring the footer.
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getIntrinsicContentHeight() {
return mIntrinsicContentHeight;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void updateClipping() {
boolean animatingClipping = mInterpolatedDarkAmount > 0 && mInterpolatedDarkAmount < 1;
boolean clipped = mRequestedClipBounds != null && !mInHeadsUpPinnedMode
&& !mHeadsUpAnimatingAway;
if (mIsClipped != clipped) {
mIsClipped = clipped;
- updateFadingState();
}
if (animatingClipping) {
@@ -1144,6 +1173,7 @@
* @return The translation at the beginning when expanding.
* Measured relative to the resting position.
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private float getExpandTranslationStart() {
return -mTopPadding + getMinExpansionHeight();
}
@@ -1152,6 +1182,7 @@
* @return the position from where the appear transition starts when expanding.
* Measured in absolute height.
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private float getAppearStartPosition() {
if (isHeadsUpTransition()) {
return mHeadsUpInset + mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
@@ -1164,6 +1195,7 @@
* intrinsic height, which also includes whether the notification is system expanded and
* is mainly used when dragging down from a heads up notification.
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getTopHeadsUpPinnedHeight() {
NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
if (topEntry == null) {
@@ -1184,6 +1216,7 @@
* @return the position from where the appear transition ends when expanding.
* Measured in absolute height.
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private float getAppearEndPosition() {
int appearPosition;
int notGoneChildCount = getNotGoneChildCount();
@@ -1203,6 +1236,7 @@
return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean isHeadsUpTransition() {
return mTrackingHeadsUp && mFirstVisibleBackgroundChild != null
&& mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild);
@@ -1212,6 +1246,7 @@
* @param height the height of the panel
* @return the fraction of the appear animation that has been performed
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getAppearFraction(float height) {
float appearEndPosition = getAppearEndPosition();
float appearStartPosition = getAppearStartPosition();
@@ -1219,10 +1254,12 @@
/ (appearEndPosition - appearStartPosition);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getStackTranslation() {
return mStackTranslation;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setStackTranslation(float stackTranslation) {
if (stackTranslation != mStackTranslation) {
mStackTranslation = stackTranslation;
@@ -1237,19 +1274,23 @@
*
* @return either the layout height or the externally defined height, whichever is smaller
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private int getLayoutHeight() {
return Math.min(mMaxLayoutHeight, mCurrentStackHeight);
}
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
public int getFirstItemMinHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
mLongPressListener = listener;
}
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
public void setQsContainer(ViewGroup qsContainer) {
mQsContainer = qsContainer;
}
@@ -1259,6 +1300,7 @@
* re-invoking dismiss logic in case the notification has not made its way out yet).
*/
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onChildDismissed(View view) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (!row.isDismissed()) {
@@ -1276,6 +1318,8 @@
*
* @param view view (e.g. notification) to dismiss from the layout
*/
+
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void handleChildViewDismissed(View view) {
if (mDismissAllInProgress) {
return;
@@ -1315,6 +1359,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onChildSnappedBack(View animView, float targetLeft) {
mAmbientState.onDragFinished(animView);
updateContinuousShadowDrawing();
@@ -1335,12 +1380,14 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean updateSwipeProgress(View animView, boolean dismissable, float swipeProgress) {
// Returning true prevents alpha fading.
return !mFadeNotificationsOnDismiss;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void onBeginDrag(View v) {
mFalsingManager.onNotificatonStartDismissing();
setSwipingInProgress(true);
@@ -1353,6 +1400,7 @@
requestChildrenUpdate();
}
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
public static boolean isPinnedHeadsUp(View v) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -1361,6 +1409,7 @@
return false;
}
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
private boolean isHeadsUp(View v) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -1370,17 +1419,20 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void onDragCancelled(View v) {
mFalsingManager.onNotificatonStopDismissing();
setSwipingInProgress(false);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public float getFalsingThresholdFactor() {
return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public View getChildAtPosition(MotionEvent ev) {
View child = getChildAtPosition(ev.getX(), ev.getY());
if (child instanceof ExpandableNotificationRow) {
@@ -1401,6 +1453,7 @@
return child;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public ExpandableView getClosestChildAtRawPosition(float touchX, float touchY) {
getLocationOnScreen(mTempInt2);
float localTouchY = touchY - mTempInt2[1];
@@ -1431,12 +1484,14 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public ExpandableView getChildAtRawPosition(float touchX, float touchY) {
getLocationOnScreen(mTempInt2);
return getChildAtPosition(touchX - mTempInt2[0], touchY - mTempInt2[1]);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public ExpandableView getChildAtPosition(float touchX, float touchY) {
return getChildAtPosition(touchX, touchY, true /* requireMinHeight */);
@@ -1450,6 +1505,7 @@
* @param requireMinHeight Whether a minimum height is required for a child to be returned.
* @return the child at the given location.
*/
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private ExpandableView getChildAtPosition(float touchX, float touchY,
boolean requireMinHeight) {
// find the view under the pointer, accounting for GONE views
@@ -1490,6 +1546,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
public boolean canChildBeExpanded(View v) {
return v instanceof ExpandableNotificationRow
&& ((ExpandableNotificationRow) v).isExpandable()
@@ -1499,6 +1556,7 @@
/* Only ever called as a consequence of an expansion gesture in the shade. */
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setUserExpandedChild(View v, boolean userExpanded) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -1521,6 +1579,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setExpansionCancelled(View v) {
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setGroupExpansionChanging(false);
@@ -1528,6 +1587,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setUserLockedChild(View v, boolean userLocked) {
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setUserLocked(userLocked);
@@ -1537,6 +1597,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void expansionStateChanged(boolean isExpanding) {
mExpandingNotification = isExpanding;
if (!mExpandedInThisMotion) {
@@ -1546,14 +1607,17 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getMaxExpandHeight(ExpandableView view) {
return view.getMaxContentHeight();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setScrollingEnabled(boolean enable) {
mScrollingEnabled = enable;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void lockScrollTo(View v) {
if (mForcedScroll == v) {
return;
@@ -1562,6 +1626,7 @@
scrollTo(v);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean scrollTo(View v) {
ExpandableView expandableView = (ExpandableView) v;
int positionInLinearLayout = getPositionInLinearLayout(v);
@@ -1583,6 +1648,7 @@
* @return the scroll necessary to make the bottom edge of {@param v} align with the top of
* the IME.
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int targetScrollForView(ExpandableView v, int positionInLinearLayout) {
return positionInLinearLayout + v.getIntrinsicHeight() +
getImeInset() - getHeight()
@@ -1590,6 +1656,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mBottomInset = insets.getSystemWindowInsetBottom();
@@ -1607,6 +1674,7 @@
return insets;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private Runnable mReclamp = new Runnable() {
@Override
public void run() {
@@ -1618,28 +1686,34 @@
}
};
- private void setExpandingEnabled(boolean enable) {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void setExpandingEnabled(boolean enable) {
mExpandHelper.setEnabled(enable);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean isScrollingEnabled() {
return mScrollingEnabled;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
public boolean canChildBeDismissed(View v) {
return StackScrollAlgorithm.canChildBeDismissed(v);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean isAntiFalsingNeeded() {
return onKeyguard();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean onKeyguard() {
return mStatusBarState == StatusBarState.KEYGUARD;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void setSwipingInProgress(boolean isSwiped) {
mSwipingInProgress = isSwiped;
if (isSwiped) {
@@ -1648,6 +1722,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
@@ -1658,12 +1733,14 @@
initView(getContext());
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
true /* isDismissAll */);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void snapViewIfNeeded(ExpandableNotificationRow child) {
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
// If the child is showing the notification menu snap to that
@@ -1672,11 +1749,13 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
return this;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean onTouchEvent(MotionEvent ev) {
boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
|| ev.getActionMasked() == MotionEvent.ACTION_UP;
@@ -1725,6 +1804,7 @@
return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void dispatchDownEventToScroller(MotionEvent ev) {
MotionEvent downEvent = MotionEvent.obtain(ev);
downEvent.setAction(MotionEvent.ACTION_DOWN);
@@ -1733,6 +1813,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean onGenericMotionEvent(MotionEvent event) {
if (!isScrollingEnabled() || !mIsExpanded || mSwipingInProgress || mExpandingNotification
|| mDisallowScrollingInThisMotion) {
@@ -1765,6 +1846,7 @@
return super.onGenericMotionEvent(event);
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private boolean onScrollTouch(MotionEvent ev) {
if (!isScrollingEnabled()) {
return false;
@@ -1901,10 +1983,12 @@
return true;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
protected boolean isInsideQsContainer(MotionEvent ev) {
return ev.getY() < mQsContainer.getBottom();
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void onOverScrollFling(boolean open, int initialVelocity) {
if (mOverscrollTopChangedListener != null) {
mOverscrollTopChangedListener.flingTopOverscroll(initialVelocity, open);
@@ -1920,6 +2004,7 @@
* @return The amount of scrolling to be performed by the scroller,
* not handled by the overScroll amount.
*/
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private float overScrollUp(int deltaY, int range) {
deltaY = Math.max(deltaY, 0);
float currentTopAmount = getCurrentOverScrollAmount(true);
@@ -1953,6 +2038,7 @@
* @return The amount of scrolling to be performed by the scroller,
* not handled by the overScroll amount.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private float overScrollDown(int deltaY) {
deltaY = Math.min(deltaY, 0);
float currentBottomAmount = getCurrentOverScrollAmount(false);
@@ -1977,6 +2063,7 @@
return scrollAmount;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
MotionEvent.ACTION_POINTER_INDEX_SHIFT;
@@ -1994,12 +2081,14 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
@@ -2007,6 +2096,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void initOrResetVelocityTracker() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
@@ -2015,10 +2105,12 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setFinishScrollingCallback(Runnable runnable) {
mFinishScrollingCallback = runnable;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void animateScroll() {
if (mScroller.computeScrollOffset()) {
int oldY = mOwnScrollY;
@@ -2049,6 +2141,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean customOverScrollBy(int deltaY, int scrollY, int scrollRangeY,
int maxOverScrollY) {
@@ -2080,6 +2173,7 @@
* @param onTop Should the effect be applied on top of the scroller.
* @param animate Should an animation be performed.
*/
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
}
@@ -2092,6 +2186,8 @@
* @param onTop Should the effect be applied on top of the scroller.
* @param animate Should an animation be performed.
*/
+
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
setOverScrollAmount(amount, onTop, animate, true);
}
@@ -2104,6 +2200,7 @@
* @param animate Should an animation be performed.
* @param cancelAnimators Should running animations be cancelled.
*/
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
boolean cancelAnimators) {
setOverScrollAmount(amount, onTop, animate, cancelAnimators, isRubberbanded(onTop));
@@ -2119,6 +2216,7 @@
* @param isRubberbanded The value which will be passed to
* {@link OnOverscrollTopChangedListener#onOverscrollTopChanged}
*/
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
boolean cancelAnimators, boolean isRubberbanded) {
if (cancelAnimators) {
@@ -2127,6 +2225,7 @@
setOverScrollAmountInternal(amount, onTop, animate, isRubberbanded);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate,
boolean isRubberbanded) {
amount = Math.max(0, amount);
@@ -2142,6 +2241,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void notifyOverscrollTopListener(float amount, boolean isRubberbanded) {
mExpandHelper.onlyObserveMovements(amount > 1.0f);
if (mDontReportNextOverScroll) {
@@ -2153,19 +2253,23 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setOverscrollTopChangedListener(
OnOverscrollTopChangedListener overscrollTopChangedListener) {
mOverscrollTopChangedListener = overscrollTopChangedListener;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getCurrentOverScrollAmount(boolean top) {
return mAmbientState.getOverScrollAmount(top);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getCurrentOverScrolledPixels(boolean top) {
return top ? mOverScrolledTopPixels : mOverScrolledBottomPixels;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setOverScrolledPixels(float amount, boolean onTop) {
if (onTop) {
mOverScrolledTopPixels = amount;
@@ -2174,6 +2278,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void onCustomOverScrolled(int scrollY, boolean clampedY) {
// Treat animating scrolls differently; see #computeScroll() for why.
if (!mScroller.isFinished()) {
@@ -2193,6 +2298,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void springBack() {
int scrollRange = getScrollRange();
boolean overScrolledTop = mOwnScrollY <= 0;
@@ -2216,6 +2322,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getScrollRange() {
// In current design, it only use the top HUN to treat all of HUNs
// although there are more than one HUNs
@@ -2229,6 +2336,7 @@
return scrollRange;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getImeInset() {
return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
}
@@ -2236,6 +2344,7 @@
/**
* @return the first child which has visibility unequal to GONE
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public ExpandableView getFirstChildNotGone() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -2250,6 +2359,7 @@
/**
* @return the child before the given view which has visibility unequal to GONE
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public ExpandableView getViewBeforeView(ExpandableView view) {
ExpandableView previousView = null;
int childCount = getChildCount();
@@ -2269,6 +2379,7 @@
* @return The first child which has visibility unequal to GONE which is currently below the
* given translationY or equal to it.
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private View getFirstChildBelowTranlsationY(float translationY, boolean ignoreChildren) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -2300,6 +2411,7 @@
/**
* @return the last child which has visibility unequal to GONE
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public View getLastChildNotGone() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
@@ -2314,6 +2426,7 @@
/**
* @return the number of children which have visibility unequal to GONE
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getNotGoneChildCount() {
int childCount = getChildCount();
int count = 0;
@@ -2326,6 +2439,7 @@
return count;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateContentHeight() {
int height = 0;
float previousPaddingRequest = mPaddingBetweenElements;
@@ -2399,15 +2513,18 @@
mAmbientState.setLayoutMaxHeight(mContentHeight);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private boolean isPulsing(NotificationData.Entry entry) {
return mAmbientState.isPulsing(entry);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean hasPulsingNotifications() {
return mPulsing;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateScrollability() {
boolean scrollable = !mQsExpanded && getScrollRange() > 0;
if (scrollable != mScrollable) {
@@ -2417,6 +2534,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateForwardAndBackwardScrollability() {
boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
@@ -2429,6 +2547,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateBackground() {
// No need to update the background color if it's not being drawn.
if (!mShouldDrawNotificationBackground || mAmbientState.isFullyDark()) {
@@ -2447,7 +2566,7 @@
startBackgroundAnimation();
} else {
mCurrentBounds.set(mBackgroundBounds);
- applyCurrentBackgroundBounds();
+ invalidate();
}
} else {
abortBackgroundAnimators();
@@ -2456,6 +2575,7 @@
mAnimateNextBackgroundTop = false;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void abortBackgroundAnimators() {
if (mBottomAnimator != null) {
mBottomAnimator.cancel();
@@ -2465,10 +2585,12 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean areBoundsAnimating() {
return mBottomAnimator != null || mTopAnimator != null;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void startBackgroundAnimation() {
// left and right are always instantly applied
mCurrentBounds.left = mBackgroundBounds.left;
@@ -2477,6 +2599,7 @@
startTopAnimation();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void startTopAnimation() {
int previousEndValue = mEndAnimationRect.top;
int newEndValue = mBackgroundBounds.top;
@@ -2525,6 +2648,7 @@
mTopAnimator = animator;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void startBottomAnimation() {
int previousStartValue = mStartAnimationRect.bottom;
int previousEndValue = mEndAnimationRect.bottom;
@@ -2573,33 +2697,22 @@
mBottomAnimator = animator;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void setBackgroundTop(int top) {
mCurrentBounds.top = top;
- applyCurrentBackgroundBounds();
+ invalidate();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setBackgroundBottom(int bottom) {
mCurrentBounds.bottom = bottom;
- applyCurrentBackgroundBounds();
- }
-
- private void applyCurrentBackgroundBounds() {
- // If the background of the notification is not being drawn, then there is no need to
- // exclude an area in the scrim. Rather, the scrim's color should serve as the background.
- if (!mShouldDrawNotificationBackground) {
- return;
- }
-
- final boolean awake = mInterpolatedDarkAmount != 0 || mAmbientState.isDark();
- mScrimController.setExcludedBackgroundArea(
- mFadingOut || mParentNotFullyVisible || awake || mIsClipped ? null
- : mCurrentBounds);
invalidate();
}
/**
* Update the background bounds to the new desired bounds
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateBackgroundBounds() {
getLocationInWindow(mTempInt2);
mBackgroundBounds.left = mTempInt2[0] + mSidePaddings;
@@ -2661,6 +2774,7 @@
mBackgroundBounds.bottom = Math.max(bottom, top);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private ActivatableNotificationView getFirstPinnedHeadsUp() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -2676,6 +2790,7 @@
return null;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private ActivatableNotificationView getLastChildWithBackground() {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
@@ -2688,6 +2803,7 @@
return null;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private ActivatableNotificationView getFirstChildWithBackground() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -2707,6 +2823,7 @@
* numbers mean that the finger/cursor is moving down the screen,
* which means we want to scroll towards the top.
*/
+ @ShadeViewRefactor(RefactorComponent.INPUT)
protected void fling(int velocityY) {
if (getChildCount() > 0) {
int scrollRange = getScrollRange();
@@ -2744,6 +2861,7 @@
* @return Whether a fling performed on the top overscroll edge lead to the expanded
* overScroll view (i.e QS).
*/
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private boolean shouldOverScrollFling(int initialVelocity) {
float topOverScroll = getCurrentOverScrollAmount(true);
return mScrolledToTopOnFirstDown
@@ -2761,6 +2879,7 @@
* @param ignoreIntrinsicPadding if true, {@link #getIntrinsicPadding()} is ignored and
* {@code qsHeight} is the final top padding
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void updateTopPadding(float qsHeight, boolean animate,
boolean ignoreIntrinsicPadding) {
int topPadding = (int) qsHeight;
@@ -2775,10 +2894,12 @@
setExpandedHeight(mExpandedHeight);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setMaxTopPadding(int maxTopPadding) {
mMaxTopPadding = maxTopPadding;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getLayoutMinHeight() {
if (isHeadsUpTransition()) {
return getTopHeadsUpPinnedHeight();
@@ -2786,6 +2907,7 @@
return mShelf.getVisibility() == GONE ? 0 : mShelf.getIntrinsicHeight();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getFirstChildIntrinsicHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
int firstChildMinHeight = firstChild != null
@@ -2799,10 +2921,13 @@
return firstChildMinHeight;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getTopPaddingOverflow() {
return mTopPaddingOverflow;
}
+
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getPeekHeight() {
final ExpandableView firstChild = getFirstChildNotGone();
final int firstChildMinHeight = firstChild != null ? firstChild.getCollapsedHeight()
@@ -2814,10 +2939,12 @@
return mIntrinsicPadding + firstChildMinHeight + shelfHeight;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int clampPadding(int desiredPadding) {
return Math.max(desiredPadding, mIntrinsicPadding);
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private float getRubberBandFactor(boolean onTop) {
if (!onTop) {
return RUBBER_BAND_FACTOR_NORMAL;
@@ -2837,11 +2964,13 @@
* rubberbanded, false if it is technically an overscroll but rather a motion to expand the
* overscroll view (e.g. expand QS).
*/
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private boolean isRubberbanded(boolean onTop) {
return !onTop || mExpandedInThisMotion || mIsExpansionChanging || mPanelTracking
|| !mScrolledToTopOnFirstDown;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void endDrag() {
setIsBeingDragged(false);
@@ -2855,12 +2984,14 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void transformTouchEvent(MotionEvent ev, View sourceView, View targetView) {
ev.offsetLocation(sourceView.getX(), sourceView.getY());
ev.offsetLocation(-targetView.getX(), -targetView.getY());
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean onInterceptTouchEvent(MotionEvent ev) {
initDownStates(ev);
handleEmptySpaceClick(ev);
@@ -2896,6 +3027,7 @@
return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void handleEmptySpaceClick(MotionEvent ev) {
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
@@ -2913,6 +3045,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void initDownStates(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mExpandedInThisMotion = false;
@@ -2925,10 +3058,12 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setChildTransferInProgress(boolean childTransferInProgress) {
mChildTransferInProgress = childTransferInProgress;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
@@ -2939,6 +3074,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@Override
public void cleanUpViewState(View child) {
if (child == mTranslatingParentView) {
@@ -2948,6 +3084,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
super.requestDisallowInterceptTouchEvent(disallowIntercept);
if (disallowIntercept) {
@@ -2955,6 +3092,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void onViewRemovedInternal(View child, ViewGroup container) {
if (mChangePositionInProgress) {
// This is only a position change, don't do anything special
@@ -2979,6 +3117,7 @@
focusNextViewIfFocused(child);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void focusNextViewIfFocused(View view) {
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
@@ -2998,6 +3137,7 @@
}
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
private boolean isChildInGroup(View child) {
return child instanceof ExpandableNotificationRow
&& mGroupManager.isChildInGroupWithSummary(
@@ -3010,6 +3150,7 @@
* @param child The view to generate the remove animation for.
* @return Whether an animation was generated.
*/
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean generateRemoveAnimation(View child) {
if (removeRemovedChildFromHeadsUpChangeAnimations(child)) {
mAddedHeadsUpChildren.remove(child);
@@ -3035,6 +3176,7 @@
return false;
}
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
private boolean isClickedHeadsUp(View child) {
return HeadsUpUtil.isClickedHeadsUpNotification(child);
}
@@ -3044,6 +3186,7 @@
*
* @return whether any child was removed from the list to animate
*/
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private boolean removeRemovedChildFromHeadsUpChangeAnimations(View child) {
boolean hasAddEvent = false;
for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
@@ -3068,6 +3211,7 @@
* @return whether a view is not a top level child but a child notification and that group is
* not expanded
*/
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
private boolean isChildInInvisibleGroup(View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
@@ -3085,6 +3229,7 @@
*
* @param removedChild the removed child
*/
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateScrollStateForRemovedChild(ExpandableView removedChild) {
int startingPosition = getPositionInLinearLayout(removedChild);
float increasedPaddingAmount = removedChild.getIncreasedPaddingAmount();
@@ -3113,6 +3258,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getIntrinsicHeight(View view) {
if (view instanceof ExpandableView) {
ExpandableView expandableView = (ExpandableView) view;
@@ -3121,6 +3267,7 @@
return view.getHeight();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getPositionInLinearLayout(View requestedView) {
ExpandableNotificationRow childInGroup = null;
ExpandableNotificationRow requestedRow = null;
@@ -3182,11 +3329,13 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onViewAdded(View child) {
super.onViewAdded(child);
onViewAddedInternal(child);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateFirstAndLastBackgroundViews() {
ActivatableNotificationView firstChild = getFirstChildWithBackground();
ActivatableNotificationView lastChild = getLastChildWithBackground();
@@ -3205,6 +3354,7 @@
invalidate();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void onViewAddedInternal(View child) {
updateHideSensitiveForChild(child);
((ExpandableView) child).setOnHeightChangedListener(this);
@@ -3213,6 +3363,7 @@
updateChronometerForChild(child);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void updateHideSensitiveForChild(View child) {
if (child instanceof ExpandableView) {
ExpandableView expandableView = (ExpandableView) child;
@@ -3221,15 +3372,18 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void notifyGroupChildRemoved(View row, ViewGroup childrenContainer) {
onViewRemovedInternal(row, childrenContainer);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void notifyGroupChildAdded(View row) {
onViewAddedInternal(row);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setAnimationsEnabled(boolean animationsEnabled) {
mAnimationsEnabled = animationsEnabled;
updateNotificationAnimationStates();
@@ -3240,6 +3394,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateNotificationAnimationStates() {
boolean running = mAnimationsEnabled || hasPulsingNotifications();
mShelf.setAnimationsEnabled(running);
@@ -3251,18 +3406,21 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateAnimationState(View child) {
updateAnimationState((mAnimationsEnabled || hasPulsingNotifications())
&& (mIsExpanded || isPinnedHeadsUp(child)), child);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setExpandingNotification(ExpandableNotificationRow row) {
mAmbientState.setExpandingNotification(row);
requestChildrenUpdate();
}
@Override
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
public void bindRow(ExpandableNotificationRow row) {
row.setHeadsUpAnimatingAwayListener(animatingAway -> {
mRoundnessManager.onHeadsupAnimatingAwayChanged(row, animatingAway);
@@ -3271,11 +3429,13 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
requestChildrenUpdate();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateAnimationState(boolean running, View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
@@ -3283,12 +3443,14 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public boolean isAddOrRemoveAnimationPending() {
return mNeedsAnimation
&& (!mChildrenToAddAnimated.isEmpty() || !mChildrenToRemoveAnimated.isEmpty());
}
@Override
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void generateAddAnimation(View child, boolean fromMoreCard) {
if (mIsExpanded && mAnimationsEnabled && !mChangePositionInProgress) {
// Generate Animations
@@ -3305,6 +3467,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void changeViewPosition(View child, int newIndex) {
int currentIndex = indexOfChild(child);
@@ -3336,6 +3499,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void startAnimationToState() {
if (mNeedsAnimation) {
generateAllAnimationEvents();
@@ -3355,6 +3519,7 @@
mGoToFullShadeDelay = 0;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateAllAnimationEvents() {
generateHeadsUpAnimationEvents();
generateChildRemovalEvents();
@@ -3374,6 +3539,7 @@
generatePulsingAnimationEvent();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateHeadsUpAnimationEvents() {
for (Pair<ExpandableNotificationRow, Boolean> eventPair : mHeadsUpChangeAnimations) {
ExpandableNotificationRow row = eventPair.first;
@@ -3416,6 +3582,7 @@
mAddedHeadsUpChildren.clear();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private boolean shouldHunAppearFromBottom(ExpandableViewState viewState) {
if (viewState.yTranslation + viewState.height < mAmbientState.getMaxHeadsUpTranslation()) {
return false;
@@ -3423,6 +3590,7 @@
return true;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateGroupExpansionEvent() {
// Generate a group expansion/collapsing event if there is such a group at all
if (mExpandedGroupView != null) {
@@ -3432,6 +3600,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateViewResizeEvent() {
if (mNeedViewResizeAnimation) {
boolean hasDisappearAnimation = false;
@@ -3452,6 +3621,7 @@
mNeedViewResizeAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateSnapBackEvents() {
for (View child : mSnappedBackChildren) {
mAnimationEvents.add(new AnimationEvent(child,
@@ -3460,6 +3630,7 @@
mSnappedBackChildren.clear();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateDragEvents() {
for (View child : mDragAnimPendingChildren) {
mAnimationEvents.add(new AnimationEvent(child,
@@ -3468,6 +3639,7 @@
mDragAnimPendingChildren.clear();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateChildRemovalEvents() {
for (View child : mChildrenToRemoveAnimated) {
boolean childWasSwipedOut = mSwipedOutViews.contains(child);
@@ -3509,6 +3681,7 @@
mChildrenToRemoveAnimated.clear();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generatePositionChangeEvents() {
for (View child : mChildrenChangingPositions) {
mAnimationEvents.add(new AnimationEvent(child,
@@ -3522,6 +3695,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateChildAdditionEvents() {
for (View child : mChildrenToAddAnimated) {
if (mFromMoreCardAdditions.contains(child)) {
@@ -3537,6 +3711,7 @@
mFromMoreCardAdditions.clear();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateTopPaddingEvent() {
if (mTopPaddingNeedsAnimation) {
AnimationEvent event;
@@ -3553,6 +3728,7 @@
mTopPaddingNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateActivateEvent() {
if (mActivateNeedsAnimation) {
mAnimationEvents.add(
@@ -3561,6 +3737,7 @@
mActivateNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateAnimateEverythingEvent() {
if (mEverythingNeedsAnimation) {
mAnimationEvents.add(
@@ -3569,6 +3746,7 @@
mEverythingNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateDimmedEvent() {
if (mDimmedNeedsAnimation) {
mAnimationEvents.add(
@@ -3577,6 +3755,7 @@
mDimmedNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateHideSensitiveEvent() {
if (mHideSensitiveNeedsAnimation) {
mAnimationEvents.add(
@@ -3585,6 +3764,7 @@
mHideSensitiveNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateDarkEvent() {
if (mDarkNeedsAnimation) {
AnimationEvent ev = new AnimationEvent(null,
@@ -3598,6 +3778,7 @@
mDarkNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void generateGoToFullShadeEvent() {
if (mGoToFullShadeNeedsAnimation) {
mAnimationEvents.add(
@@ -3606,6 +3787,7 @@
mGoToFullShadeNeedsAnimation = false;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private boolean onInterceptTouchEventScroll(MotionEvent ev) {
if (!isScrollingEnabled()) {
return false;
@@ -3715,6 +3897,7 @@
return mIsBeingDragged;
}
+ @ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
protected StackScrollAlgorithm createStackScrollAlgorithm(Context context) {
return new StackScrollAlgorithm(context);
}
@@ -3722,6 +3905,7 @@
/**
* @return Whether the specified motion event is actually happening over the content.
*/
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private boolean isInContentBounds(MotionEvent event) {
return isInContentBounds(event.getY());
}
@@ -3729,10 +3913,12 @@
/**
* @return Whether a y coordinate is inside the content.
*/
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean isInContentBounds(float y) {
return y < getHeight() - getEmptyBottomMargin();
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private void setIsBeingDragged(boolean isDragged) {
mIsBeingDragged = isDragged;
if (isDragged) {
@@ -3742,6 +3928,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
@@ -3750,6 +3937,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void clearChildFocus(View child) {
super.clearChildFocus(child);
if (mForcedScroll == child) {
@@ -3757,37 +3945,45 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void requestDisallowLongPress() {
cancelLongPress();
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void requestDisallowDismiss() {
mDisallowDismissInThisMotion = true;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void cancelLongPress() {
mSwipeHelper.cancelLongPress();
}
@Override
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public boolean isScrolledToTop() {
return mOwnScrollY == 0;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public boolean isScrolledToBottom() {
return mOwnScrollY >= getScrollRange();
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public View getHostView() {
return this;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getEmptyBottomMargin() {
return Math.max(mMaxLayoutHeight - mContentHeight, 0);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void checkSnoozeLeavebehind() {
if (mCheckForLeavebehind) {
mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
@@ -3797,16 +3993,19 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void resetCheckSnoozeLeavebehind() {
mCheckForLeavebehind = true;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onExpansionStarted() {
mIsExpansionChanging = true;
mAmbientState.setExpansionChanging(true);
checkSnoozeLeavebehind();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onExpansionStopped() {
mIsExpansionChanging = false;
resetCheckSnoozeLeavebehind();
@@ -3819,6 +4018,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void clearUserLockedViews() {
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = (ExpandableView) getChildAt(i);
@@ -3829,6 +4029,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void clearTemporaryViews() {
// lets make sure nothing is transient anymore
clearTemporaryViewsInGroup(this);
@@ -3841,27 +4042,32 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
viewGroup.removeTransientView(viewGroup.getTransientView(0));
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onPanelTrackingStarted() {
mPanelTracking = true;
mAmbientState.setPanelTracking(true);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onPanelTrackingStopped() {
mPanelTracking = false;
mAmbientState.setPanelTracking(false);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void resetScrollPosition() {
mScroller.abortAnimation();
setOwnScrollY(0);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void setIsExpanded(boolean isExpanded) {
boolean changed = isExpanded != mIsExpanded;
mIsExpanded = isExpanded;
@@ -3877,6 +4083,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void updateChronometers() {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -3884,6 +4091,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
private void updateChronometerForChild(View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
@@ -3892,6 +4100,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
updateContentHeight();
updateScrollPositionOnExpandInBottom(view);
@@ -3911,11 +4120,13 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onReset(ExpandableView view) {
updateAnimationState(view);
updateChronometerForChild(view);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
if (view instanceof ExpandableNotificationRow && !onKeyguard()) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
@@ -3940,15 +4151,18 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setOnHeightChangedListener(
ExpandableView.OnHeightChangedListener mOnHeightChangedListener) {
this.mOnHeightChangedListener = mOnHeightChangedListener;
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void setOnEmptySpaceClickListener(OnEmptySpaceClickListener listener) {
mOnEmptySpaceClickListener = listener;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onChildAnimationFinished() {
setAnimationRunning(false);
requestChildrenUpdate();
@@ -3957,6 +4171,7 @@
clearHeadsUpDisappearRunning();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void clearHeadsUpDisappearRunning() {
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
@@ -3972,6 +4187,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void clearTransient() {
for (ExpandableView view : mClearTransientViewsWhenFinished) {
StackStateAnimator.removeTransientView(view);
@@ -3979,6 +4195,7 @@
mClearTransientViewsWhenFinished.clear();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void runAnimationFinishedRunnables() {
for (Runnable runnable : mAnimationFinishedRunnables) {
runnable.run();
@@ -3989,6 +4206,7 @@
/**
* See {@link AmbientState#setDimmed}.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setDimmed(boolean dimmed, boolean animate) {
dimmed &= onKeyguard();
mAmbientState.setDimmed(dimmed);
@@ -4003,15 +4221,18 @@
}
@VisibleForTesting
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
boolean isDimmed() {
return mAmbientState.isDimmed();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void setDimAmount(float dimAmount) {
mDimAmount = dimAmount;
updateBackgroundDimming();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void animateDimmed(boolean dimmed) {
if (mDimAnimator != null) {
mDimAnimator.cancel();
@@ -4027,8 +4248,8 @@
mDimAnimator.addUpdateListener(mDimUpdateListener);
mDimAnimator.start();
}
-
- private void setHideSensitive(boolean hideSensitive, boolean animate) {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void setHideSensitive(boolean hideSensitive, boolean animate) {
if (hideSensitive != mAmbientState.isHideSensitive()) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -4048,6 +4269,7 @@
/**
* See {@link AmbientState#setActivatedChild}.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setActivatedChild(ActivatableNotificationView activatedChild) {
mAmbientState.setActivatedChild(activatedChild);
if (mAnimationsEnabled) {
@@ -4057,10 +4279,12 @@
requestChildrenUpdate();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public ActivatableNotificationView getActivatedChild() {
return mAmbientState.getActivatedChild();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void applyCurrentState() {
mCurrentStackScrollState.apply();
if (mListener != null) {
@@ -4073,6 +4297,7 @@
updateClippingToTopRoundedCorner();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateViewShadows() {
// we need to work around an issue where the shadow would not cast between siblings when
// their z difference is between 0 and 0.1
@@ -4115,6 +4340,7 @@
*
* @param lightTheme True if light theme should be used.
*/
+ @ShadeViewRefactor(RefactorComponent.DECORATOR)
public void updateDecorViews(boolean lightTheme) {
if (lightTheme == mUsingLightTheme) {
return;
@@ -4127,6 +4353,7 @@
mEmptyShadeView.setTextColor(textColor);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void goToFullShade(long delay) {
mGoToFullShadeNeedsAnimation = true;
mGoToFullShadeDelay = delay;
@@ -4134,15 +4361,18 @@
requestChildrenUpdate();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void cancelExpandHelper() {
mExpandHelper.cancel();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setIntrinsicPadding(int intrinsicPadding) {
mIntrinsicPadding = intrinsicPadding;
mAmbientState.setIntrinsicPadding(intrinsicPadding);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getIntrinsicPadding() {
return mIntrinsicPadding;
}
@@ -4150,11 +4380,13 @@
/**
* @return the y position of the first notification
*/
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getNotificationsTopY() {
return mTopPadding + getStackTranslation();
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean shouldDelayChildPressedState() {
return true;
}
@@ -4162,6 +4394,7 @@
/**
* See {@link AmbientState#setDark}.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
if (mAmbientState.isDark() == dark) {
return;
@@ -4176,15 +4409,16 @@
updateBackground();
}
requestChildrenUpdate();
- applyCurrentBackgroundBounds();
updateWillNotDraw();
notifyHeightChangeListener(mShelf);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updatePanelTranslation() {
setTranslationX(mVerticalPanelTranslation + mAntiBurnInOffsetX * mInterpolatedDarkAmount);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setVerticalPanelTranslation(float verticalPanelTranslation) {
mVerticalPanelTranslation = verticalPanelTranslation;
updatePanelTranslation();
@@ -4195,11 +4429,13 @@
* not {@link #onDraw(Canvas)} is called). This method should be called whenever the
* {@link #mAmbientState}'s dark mode is toggled.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void updateWillNotDraw() {
boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
setWillNotDraw(!willDraw);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void setDarkAmount(float darkAmount) {
setDarkAmount(darkAmount, darkAmount);
}
@@ -4213,6 +4449,7 @@
* @param interpolatedDarkAmount The dark amount that follows the actual interpolation of the
* animation curve.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setDarkAmount(float linearDarkAmount, float interpolatedDarkAmount) {
mLinearDarkAmount = linearDarkAmount;
mInterpolatedDarkAmount = interpolatedDarkAmount;
@@ -4235,6 +4472,7 @@
requestChildrenUpdate();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void notifyDarkAnimationStart(boolean dark) {
// We only swap the scaling factor if we're fully dark or fully awake to avoid
// interpolation issues when playing with the power button.
@@ -4246,6 +4484,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public long getDarkAnimationDuration(boolean dark) {
long duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
// Longer animation when sleeping with more than 1 notification
@@ -4255,6 +4494,7 @@
return duration;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
if (screenLocation == null || screenLocation.y < mTopPadding) {
return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
@@ -4270,6 +4510,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private int getNotGoneIndex(View child) {
int count = getChildCount();
int notGoneIndex = 0;
@@ -4285,6 +4526,7 @@
return -1;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setFooterView(@NonNull FooterView footerView) {
int index = -1;
if (mFooterView != null) {
@@ -4295,6 +4537,7 @@
addView(mFooterView, index);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
int index = -1;
if (mEmptyShadeView != null) {
@@ -4305,6 +4548,7 @@
addView(mEmptyShadeView, index);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateEmptyShadeView(boolean visible) {
mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled);
@@ -4316,6 +4560,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void updateFooterView(boolean visible, boolean showDismissView) {
if (mFooterView == null) {
return;
@@ -4325,12 +4570,14 @@
mFooterView.setSecondaryVisible(showDismissView, animate);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setDismissAllInProgress(boolean dismissAllInProgress) {
mDismissAllInProgress = dismissAllInProgress;
mAmbientState.setDismissAllInProgress(dismissAllInProgress);
handleDismissAllClipping();
}
+ @ShadeViewRefactor(RefactorComponent.ADAPTER)
private void handleDismissAllClipping() {
final int count = getChildCount();
boolean previousChildWillBeDismissed = false;
@@ -4348,24 +4595,29 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean isFooterViewNotGone() {
return mFooterView != null
&& mFooterView.getVisibility() != View.GONE
&& !mFooterView.willBeGone();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean isFooterViewContentVisible() {
return mFooterView != null && mFooterView.isContentVisible();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public int getFooterViewHeight() {
return mFooterView == null ? 0 : mFooterView.getHeight() + mPaddingBetweenElements;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public int getEmptyShadeViewHeight() {
return mEmptyShadeView.getHeight();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getBottomMostNotificationBottom() {
final int count = getChildCount();
float max = 0;
@@ -4383,15 +4635,18 @@
return max + getStackTranslation();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setStatusBar(StatusBar statusBar) {
this.mStatusBar = statusBar;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setGroupManager(NotificationGroupManager groupManager) {
this.mGroupManager = groupManager;
mGroupManager.setOnGroupChangeListener(this);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void requestAnimateEverything() {
if (mIsExpanded && mAnimationsEnabled) {
mEverythingNeedsAnimation = true;
@@ -4400,6 +4655,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public boolean isBelowLastNotification(float touchX, float touchY) {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
@@ -4431,6 +4687,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded) {
boolean animated = !mGroupExpandedForMeasure && mAnimationsEnabled
&& (mIsExpanded || changedRow.isPinned());
@@ -4451,12 +4708,14 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onGroupCreatedFromChildren(NotificationGroupManager.NotificationGroup group) {
mStatusBar.requestNotificationUpdate();
}
/** @hide */
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
super.onInitializeAccessibilityEventInternal(event);
event.setScrollable(mScrollable);
@@ -4467,6 +4726,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
if (mScrollable) {
@@ -4487,6 +4747,7 @@
/** @hide */
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
if (super.performAccessibilityActionInternal(action, arguments)) {
return true;
@@ -4519,10 +4780,12 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void onGroupsChanged() {
mStatusBar.requestNotificationUpdate();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void generateChildOrderChangedEvent() {
if (mIsExpanded && mAnimationsEnabled) {
mGenerateChildOrderChangedEvent = true;
@@ -4532,29 +4795,35 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public int getContainerChildCount() {
return getChildCount();
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public View getContainerChildAt(int i) {
return getChildAt(i);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void removeContainerView(View v) {
removeView(v);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void addContainerView(View v) {
addView(v);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void runAfterAnimationFinished(Runnable runnable) {
mAnimationFinishedRunnables.add(runnable);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
mAmbientState.setHeadsUpManager(headsUpManager);
@@ -4562,6 +4831,7 @@
mHeadsUpManager.setAnimationStateHandler(this);
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) {
if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) {
mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp));
@@ -4573,6 +4843,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setShadeExpanded(boolean shadeExpanded) {
mAmbientState.setShadeExpanded(shadeExpanded);
mStateAnimator.setShadeExpanded(shadeExpanded);
@@ -4585,31 +4856,37 @@
* @param height the height of the screen
* @param bottomBarHeight the height of the bar on the bottom
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpBoundaries(int height, int bottomBarHeight) {
mAmbientState.setMaxHeadsUpTranslation(height - bottomBarHeight);
mStateAnimator.setHeadsUpAppearHeightBottom(height);
requestChildrenUpdate();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setTrackingHeadsUp(ExpandableNotificationRow row) {
mTrackingHeadsUp = row != null;
mRoundnessManager.setTrackingHeadsUp(row);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setScrimController(ScrimController scrimController) {
mScrimController = scrimController;
mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void forceNoOverlappingRendering(boolean force) {
mForceNoOverlappingRendering = force;
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean hasOverlappingRendering() {
return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void setAnimationRunning(boolean animationRunning) {
if (animationRunning != mAnimationRunning) {
if (animationRunning) {
@@ -4622,10 +4899,12 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean isExpanded() {
return mIsExpanded;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setPulsing(boolean pulsing, boolean animated) {
if (!mPulsing && !pulsing) {
return;
@@ -4641,6 +4920,7 @@
mNeedsAnimation |= animated;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void generatePulsingAnimationEvent() {
if (mNeedingPulseAnimation != null) {
int type = mPulsing ? AnimationEvent.ANIMATION_TYPE_PULSE_APPEAR
@@ -4650,45 +4930,19 @@
}
}
- public void setFadingOut(boolean fadingOut) {
- if (fadingOut != mFadingOut) {
- mFadingOut = fadingOut;
- updateFadingState();
- }
- }
-
- public void setParentNotFullyVisible(boolean parentNotFullyVisible) {
- if (mScrimController == null) {
- // we're not set up yet.
- return;
- }
- if (parentNotFullyVisible != mParentNotFullyVisible) {
- mParentNotFullyVisible = parentNotFullyVisible;
- updateFadingState();
- }
- }
-
- private void updateFadingState() {
- applyCurrentBackgroundBounds();
- updateSrcDrawing();
- }
-
- @Override
- public void setAlpha(@FloatRange(from = 0.0, to = 1.0) float alpha) {
- super.setAlpha(alpha);
- setFadingOut(alpha != 1.0f);
- }
-
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setQsExpanded(boolean qsExpanded) {
mQsExpanded = qsExpanded;
updateAlgorithmLayoutMinHeight();
updateScrollability();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setQsExpansionFraction(float qsExpansionFraction) {
mQsExpansionFraction = qsExpansionFraction;
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setOwnScrollY(int ownScrollY) {
if (ownScrollY != mOwnScrollY) {
// We still want to call the normal scrolled changed for accessibility reasons
@@ -4699,6 +4953,7 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setShelf(NotificationShelf shelf) {
int index = -1;
if (mShelf != null) {
@@ -4712,10 +4967,12 @@
shelf.bind(mAmbientState, this);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public NotificationShelf getNotificationShelf() {
return mShelf;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setMaxDisplayedNotifications(int maxDisplayedNotifications) {
if (mMaxDisplayedNotifications != maxDisplayedNotifications) {
mMaxDisplayedNotifications = maxDisplayedNotifications;
@@ -4724,25 +4981,30 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) {
mShouldShowShelfOnly = shouldShowShelfOnly;
updateAlgorithmLayoutMinHeight();
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public int getMinExpansionHeight() {
return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setInHeadsUpPinnedMode(boolean inHeadsUpPinnedMode) {
mInHeadsUpPinnedMode = inHeadsUpPinnedMode;
updateClipping();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
mHeadsUpAnimatingAway = headsUpAnimatingAway;
updateClipping();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
private void setStatusBarState(int statusBarState) {
mStatusBarState = statusBarState;
mAmbientState.setStatusBarState(statusBarState);
@@ -4766,10 +5028,12 @@
onUpdateRowStates();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setExpandingVelocity(float expandingVelocity) {
mAmbientState.setExpandingVelocity(expandingVelocity);
}
+ @ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getOpeningHeight() {
if (mEmptyShadeView.getVisibility() == GONE) {
return getMinExpansionHeight();
@@ -4778,29 +5042,34 @@
}
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setIsFullWidth(boolean isFullWidth) {
mAmbientState.setPanelFullWidth(isFullWidth);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setUnlockHintRunning(boolean running) {
mAmbientState.setUnlockHintRunning(running);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setQsCustomizerShowing(boolean isShowing) {
mAmbientState.setQsCustomizerShowing(isShowing);
requestChildrenUpdate();
}
- @Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setAntiBurnInOffsetX(int antiBurnInOffsetX) {
mAntiBurnInOffsetX = antiBurnInOffsetX;
updatePanelTranslation();
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ " alpha:%f scrollY:%d maxTopPadding:%d showShelfOnly=%s"
@@ -4818,6 +5087,7 @@
mQsExpansionFraction));
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public boolean isFullyDark() {
return mAmbientState.isFullyDark();
}
@@ -4828,6 +5098,7 @@
*
* @param listener the listener to notify.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void addOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
mExpandedHeightListeners.add(listener);
}
@@ -4835,24 +5106,29 @@
/**
* Stop a listener from listening to the expandedHeight.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void removeOnExpandedHeightListener(BiConsumer<Float, Float> listener) {
mExpandedHeightListeners.remove(listener);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpAppearanceController(
HeadsUpAppearanceController headsUpAppearanceController) {
mHeadsUpAppearanceController = headsUpAppearanceController;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setIconAreaController(NotificationIconAreaController controller) {
mIconAreaController = controller;
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void manageNotifications(View v) {
Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void clearAllNotifications() {
// animate-swipe all dismissable notifications, then animate the shade closed
int numChildren = getChildCount();
@@ -4915,6 +5191,7 @@
performDismissAllAnimations(viewsToHide);
}
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void performDismissAllAnimations(ArrayList<View> hideAnimatedList) {
Runnable animationFinishAction = () -> {
mStatusBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
@@ -4947,6 +5224,7 @@
}
@VisibleForTesting
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
protected void inflateFooterView() {
FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_footer, this, false);
@@ -4958,7 +5236,8 @@
setFooterView(footerView);
}
- private void inflateEmptyShadeView() {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ private void inflateEmptyShadeView() {
EmptyShadeView view = (EmptyShadeView) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_no_notifications, this, false);
view.setText(R.string.empty_shade_text);
@@ -4968,6 +5247,7 @@
/**
* Updates expanded, dimmed and locked states of notification rows.
*/
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
public void onUpdateRowStates() {
changeViewPosition(mFooterView, -1);
@@ -4988,13 +5268,15 @@
mScrimController.setNotificationCount(getNotGoneChildCount());
}
- public void setNotificationPanel(NotificationPanelView notificationPanelView) {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public void setNotificationPanel(NotificationPanelView notificationPanelView) {
mNotificationPanel = notificationPanelView;
}
/**
* A listener that is notified when the empty space below the notifications is clicked on
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public interface OnEmptySpaceClickListener {
void onEmptySpaceClicked(float x, float y);
}
@@ -5002,6 +5284,7 @@
/**
* A listener that gets notified when the overscroll at the top has changed.
*/
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public interface OnOverscrollTopChangedListener {
/**
@@ -5025,7 +5308,8 @@
void flingTopOverscroll(float velocity, boolean open);
}
- private class NotificationSwipeHelper extends SwipeHelper
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ private class NotificationSwipeHelper extends SwipeHelper
implements NotificationSwipeActionHelper {
private static final long COVER_MENU_DELAY = 4000;
private Runnable mFalsingCheck;
@@ -5221,7 +5505,8 @@
}
}
- public boolean hasActiveNotifications() {
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
+ public boolean hasActiveNotifications() {
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
@@ -5230,6 +5515,7 @@
/* Only ever called as a consequence of a lockscreen expansion gesture. */
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean onDraggedDown(View startingChild, int dragLengthY) {
if (mStatusBarState == StatusBarState.KEYGUARD
&& hasActiveNotifications() && (!mStatusBar.isDozing() || mStatusBar.isPulsing())) {
@@ -5252,6 +5538,7 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void onDragDownReset() {
setDimmed(true /* dimmed */, true /* animated */);
resetScrollPosition();
@@ -5259,27 +5546,32 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void onCrossedThreshold(boolean above) {
setDimmed(!above /* dimmed */, true /* animate */);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void onTouchSlopExceeded() {
cancelLongPress();
checkSnoozeLeavebehind();
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void setEmptyDragAmount(float amount) {
mNotificationPanel.setEmptyDragAmount(amount);
}
@Override
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public boolean isFalsingCheckNeeded() {
return mStatusBarState == StatusBarState.KEYGUARD;
}
- public void updateSpeedBumpIndex() {
+ @ShadeViewRefactor(RefactorComponent.INPUT)
+ public void updateSpeedBumpIndex() {
int speedBumpIndex = 0;
int currentIndex = 0;
final int N = getChildCount();
@@ -5299,6 +5591,7 @@
updateSpeedBumpIndex(speedBumpIndex, noAmbient);
}
+ @ShadeViewRefactor(RefactorComponent.INPUT)
private boolean isTouchInView(MotionEvent ev, View view) {
if (view == null) {
return false;
@@ -5316,6 +5609,7 @@
return ret;
}
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private void updateContinuousShadowDrawing() {
boolean continuousShadowUpdate = mAnimationRunning
|| !mAmbientState.getDraggedViews().isEmpty();
@@ -5330,15 +5624,19 @@
}
@Override
+ @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void resetExposedMenuView(boolean animate, boolean force) {
mSwipeHelper.resetExposedMenuView(animate, force);
}
+
+ @ShadeViewRefactor(RefactorComponent.INPUT)
public void closeControlsIfOutsideTouch(MotionEvent ev) {
mSwipeHelper.closeControlsIfOutsideTouch(ev);
}
- static class AnimationEvent {
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+ static class AnimationEvent {
static AnimationFilter[] FILTERS = new AnimationFilter[]{
@@ -5638,7 +5936,8 @@
}
}
- private final StateListener mStateListener = new StateListener() {
+ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
+ private final StateListener mStateListener = new StateListener() {
@Override
public void onStatePreChange(int oldState, int newState) {
if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index afd64f3..0d3ba77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -45,7 +45,7 @@
public void onDisplayBlanked() {
if (DEBUG) {
Log.d(TAG, "Pulse in, mDozing=" + mDozing + " mPulseReason="
- + DozeLog.pulseReasonToString(mPulseReason));
+ + DozeLog.reasonToString(mPulseReason));
}
if (!mDozing) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index 6150c2f..4a05989 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -258,18 +258,6 @@
return mTrackingHeadsUp;
}
- /**
- * React to the removal of the notification in the heads up.
- *
- * @return true if the notification was removed and false if it still needs to be kept around
- * for a bit since it wasn't shown long enough
- */
- @Override
- public boolean removeNotification(@NonNull String key, boolean releaseImmediately) {
- return super.removeNotification(key, canRemoveImmediately(key)
- || releaseImmediately);
- }
-
@Override
public void snooze() {
super.snooze();
@@ -405,7 +393,8 @@
return (HeadsUpEntryPhone) getTopHeadsUpEntry();
}
- private boolean canRemoveImmediately(@NonNull String key) {
+ @Override
+ protected boolean canRemoveImmediately(@NonNull String key) {
if (mSwipedOutKeys.contains(key)) {
// We always instantly dismiss views being manually swiped out.
mSwipedOutKeys.remove(key);
@@ -414,7 +403,8 @@
HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
- return headsUpEntry != topEntry || headsUpEntry.wasShownLongEnough();
+
+ return headsUpEntry == null || headsUpEntry != topEntry || super.canRemoveImmediately(key);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 33ddfde..e5e5d40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2668,32 +2668,6 @@
setLaunchingAffordance(false);
}
- @Override
- public void setAlpha(float alpha) {
- super.setAlpha(alpha);
- updateFullyVisibleState(false /* forceNotFullyVisible */);
- }
-
- /**
- * Must be called before starting a ViewPropertyAnimator alpha animation because those
- * do NOT call setAlpha and therefore don't properly update the fullyVisibleState.
- */
- public void notifyStartFading() {
- updateFullyVisibleState(true /* forceNotFullyVisible */);
- }
-
- @Override
- public void setVisibility(int visibility) {
- super.setVisibility(visibility);
- updateFullyVisibleState(false /* forceNotFullyVisible */);
- }
-
- private void updateFullyVisibleState(boolean forceNotFullyVisible) {
- mNotificationStackScroller.setParentNotFullyVisible(forceNotFullyVisible
- || getAlpha() != 1.0f
- || getVisibility() != VISIBLE);
- }
-
/**
* Set whether we are currently launching an affordance. This is currently only set when
* launched via a camera gesture.
@@ -2992,10 +2966,6 @@
mNotificationStackScroller.setActivatedChild(o);
}
- public void setParentNotFullyVisible(boolean parent) {
- mNotificationStackScroller.setParentNotFullyVisible(parent);
- }
-
public void runAfterAnimationFinished(Runnable r) {
mNotificationStackScroller.runAfterAnimationFinished(r);
}
@@ -3020,8 +2990,4 @@
mNotificationStackScroller.setScrimController(scrimController);
updateShowEmptyShadeView();
}
-
- public void setDrawBackgroundAsSrc(boolean asSrc) {
- mNotificationStackScroller.setDrawBackgroundAsSrc(asSrc);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 57e01e7..a900c14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -596,9 +596,10 @@
mContext.getString(R.string.instant_apps));
mCurrentNotifs.add(new Pair<>(pkg, userId));
String message = mContext.getString(R.string.instant_apps_message);
- PendingIntent appInfoAction = PendingIntent.getActivity(mContext, 0,
+ UserHandle user = UserHandle.of(userId);
+ PendingIntent appInfoAction = PendingIntent.getActivityAsUser(mContext, 0,
new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
- .setData(Uri.fromParts("package", pkg, null)), 0);
+ .setData(Uri.fromParts("package", pkg, null)), 0, null, user);
Action action = new Notification.Action.Builder(null, mContext.getString(R.string.app_info),
appInfoAction).build();
@@ -611,8 +612,8 @@
.addFlags(Intent.FLAG_IGNORE_EPHEMERAL)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- PendingIntent pendingIntent = PendingIntent.getActivity(mContext,
- 0 /* requestCode */, browserIntent, 0 /* flags */);
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(mContext,
+ 0 /* requestCode */, browserIntent, 0 /* flags */, null, user);
ComponentName aiaComponent = null;
try {
aiaComponent = AppGlobals.getPackageManager().getInstantAppInstallerComponent();
@@ -629,7 +630,8 @@
.putExtra(Intent.EXTRA_LONG_VERSION_CODE, appInfo.versionCode)
.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, pendingIntent);
- PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0);
+ PendingIntent webPendingIntent = PendingIntent.getActivityAsUser(mContext, 0,
+ goToWebIntent, 0, null, user);
Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web),
webPendingIntent).build();
builder.addAction(webAction);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0c361ce..e3a7b75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -685,10 +685,6 @@
return scrim.getTag(TAG_KEY_ANIM) != null;
}
- public void setDrawBehindAsSrc(boolean asSrc) {
- mScrimBehind.setDrawAsSrc(asSrc);
- }
-
@VisibleForTesting
void setOnAnimationFinished(Runnable onAnimationFinished) {
mOnAnimationFinished = onAnimationFinished;
@@ -800,10 +796,6 @@
return Handler.getMain();
}
- public void setExcludedBackgroundArea(Rect area) {
- mScrimBehind.setExcludedArea(area);
- }
-
public int getBackgroundColor() {
int color = mLockColors.getMainColor();
return Color.argb((int) (mScrimBehind.getViewAlpha() * Color.alpha(color)),
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 22a727c..7ec4db2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -328,14 +328,6 @@
/** If true, the lockscreen will show a distinct wallpaper */
private static final boolean ENABLE_LOCKSCREEN_WALLPAPER = true;
- /**
- * Never let the alpha become zero for surfaces that draw with SRC - otherwise the RenderNode
- * won't draw anything and uninitialized memory will show through
- * if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
- * libhwui.
- */
- private static final float SRC_MIN_ALPHA = 0.002f;
-
static {
boolean onlyCoreApps;
try {
@@ -485,7 +477,6 @@
protected boolean mDozing;
private boolean mDozingRequested;
- protected boolean mScrimSrcModeEnabled;
protected BackDropView mBackdrop;
protected ImageView mBackdropFront, mBackdropBack;
@@ -652,7 +643,6 @@
mVibrateOnOpening = mContext.getResources().getBoolean(
R.bool.config_vibrateOnIconAnimation);
mVibratorHelper = Dependency.get(VibratorHelper.class);
- mScrimSrcModeEnabled = res.getBoolean(R.bool.config_status_bar_scrim_behind_use_src);
DateTimeView.setReceiverHandler(Dependency.get(Dependency.TIME_TICK_HANDLER));
putComponent(StatusBar.class, this);
@@ -800,7 +790,7 @@
this,
mNotificationPanel,
notifListContainer);
- mGutsManager.setUpWithPresenter(this, mEntryManager, notifListContainer, mCheckSaveListener,
+ mGutsManager.setUpWithPresenter(this, notifListContainer, mCheckSaveListener,
key -> {
try {
mBarService.onNotificationSettingsViewed(key);
@@ -948,15 +938,6 @@
}
}, DozeParameters.getInstance(mContext),
mContext.getSystemService(AlarmManager.class));
- if (mScrimSrcModeEnabled) {
- Runnable runnable = () -> {
- boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
- mScrimController.setDrawBehindAsSrc(asSrc);
- mNotificationPanel.setDrawBackgroundAsSrc(asSrc);
- };
- mBackdrop.setOnVisibilityChangedRunnable(runnable);
- runnable.run();
- }
mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
mHeadsUpManager, mNotificationIconAreaController, mScrimController);
mDozeScrimController = new DozeScrimController(mScrimController, context,
@@ -1483,7 +1464,7 @@
if (mBackdrop.getVisibility() != View.VISIBLE) {
mBackdrop.setVisibility(View.VISIBLE);
if (allowEnterAnimation) {
- mBackdrop.setAlpha(SRC_MIN_ALPHA);
+ mBackdrop.setAlpha(0);
mBackdrop.animate().alpha(1f);
} else {
mBackdrop.animate().cancel();
@@ -1501,9 +1482,6 @@
mBackdropBack.getDrawable().getConstantState()
.newDrawable(mBackdropFront.getResources()).mutate();
mBackdropFront.setImageDrawable(drawable);
- if (mScrimSrcModeEnabled) {
- mBackdropFront.getDrawable().mutate().setXfermode(mSrcOverXferMode);
- }
mBackdropFront.setAlpha(1f);
mBackdropFront.setVisibility(View.VISIBLE);
} else {
@@ -1518,9 +1496,6 @@
} else {
mBackdropBack.setImageDrawable(artworkDrawable);
}
- if (mScrimSrcModeEnabled) {
- mBackdropBack.getDrawable().mutate().setXfermode(mSrcXferMode);
- }
if (mBackdropFront.getVisibility() == View.VISIBLE) {
if (DEBUG_MEDIA) {
@@ -1553,7 +1528,7 @@
} else {
mStatusBarWindowController.setBackdropShowing(false);
mBackdrop.animate()
- .alpha(SRC_MIN_ALPHA)
+ .alpha(0)
.setInterpolator(Interpolators.ACCELERATE_DECELERATE)
.setDuration(300)
.setStartDelay(0)
@@ -1765,10 +1740,6 @@
return mMediaManager.getMediaNotificationKey();
}
- public boolean isScrimSrcModeEnabled() {
- return mScrimSrcModeEnabled;
- }
-
/**
* To be called when there's a state change in StatusBarKeyguardViewManager.
*/
@@ -1811,7 +1782,7 @@
mStatusBarWindowController.setHeadsUpShowing(false);
mHeadsUpManager.setHeadsUpGoingAway(false);
}
- mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
+ mRemoteInputManager.onPanelCollapsed();
});
}
}
@@ -1828,7 +1799,7 @@
@Override
public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
- mEntryManager.onHeadsUpStateChanged(entry, isHeadsUp);
+ mEntryManager.updateNotificationRanking(null /* rankingMap */);
if (isHeadsUp) {
mDozeServiceHost.fireNotificationHeadsUp();
@@ -1858,7 +1829,7 @@
}
if (!isExpanded) {
- mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
+ mRemoteInputManager.onPanelCollapsed();
}
}
@@ -3410,7 +3381,6 @@
updateScrimController();
updateMediaMetaData(false, true);
mNotificationPanel.setAlpha(1);
- mNotificationPanel.setParentNotFullyVisible(true);
mNotificationPanel.animate()
.alpha(0)
.setStartDelay(FADE_KEYGUARD_START_DELAY)
@@ -3432,7 +3402,6 @@
* fading.
*/
public void fadeKeyguardWhilePulsing() {
- mNotificationPanel.notifyStartFading();
mNotificationPanel.animate()
.alpha(0f)
.setStartDelay(0)
@@ -3751,7 +3720,7 @@
clearNotificationEffects();
}
if (newState == StatusBarState.KEYGUARD) {
- mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
+ mRemoteInputManager.onPanelCollapsed();
maybeEscalateHeadsUp();
}
}
@@ -4862,7 +4831,8 @@
removeNotification(parentToCancelFinal);
}
if (shouldAutoCancel(sbn)
- || mEntryManager.isNotificationKeptForRemoteInput(notificationKey)) {
+ || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
+ notificationKey)) {
// Automatically remove all notifications that we may have kept around longer
removeNotification(sbn);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 98f1a36..45b32c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -227,20 +227,7 @@
@Override
protected void onAttachedToWindow () {
super.onAttachedToWindow();
-
- // We need to ensure that our window doesn't suffer from overdraw which would normally
- // occur if our window is translucent. Since we are drawing the whole window anyway with
- // the scrim, we don't need the window to be cleared in the beginning.
- if (mService.isScrimSrcModeEnabled()) {
- IBinder windowToken = getWindowToken();
- WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
- lp.token = windowToken;
- setLayoutParams(lp);
- WindowManagerGlobal.getInstance().changeCanvasOpacity(windowToken, true);
- setWillNotDraw(false);
- } else {
- setWillNotDraw(!DEBUG);
- }
+ setWillNotDraw(!DEBUG);
}
@Override
@@ -394,26 +381,6 @@
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (mService.isScrimSrcModeEnabled()) {
- // We need to ensure that our window is always drawn fully even when we have paddings,
- // since we simulate it to be opaque.
- int paddedBottom = getHeight() - getPaddingBottom();
- int paddedRight = getWidth() - getPaddingRight();
- if (getPaddingTop() != 0) {
- canvas.drawRect(0, 0, getWidth(), getPaddingTop(), mTransparentSrcPaint);
- }
- if (getPaddingBottom() != 0) {
- canvas.drawRect(0, paddedBottom, getWidth(), getHeight(), mTransparentSrcPaint);
- }
- if (getPaddingLeft() != 0) {
- canvas.drawRect(0, getPaddingTop(), getPaddingLeft(), paddedBottom,
- mTransparentSrcPaint);
- }
- if (getPaddingRight() != 0) {
- canvas.drawRect(paddedRight, getPaddingTop(), getWidth(), paddedBottom,
- mTransparentSrcPaint);
- }
- }
if (DEBUG) {
Paint pt = new Paint();
pt.setColor(0x80FFFF00);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index dbfbdd7..15b2f2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -51,6 +51,7 @@
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
@@ -70,7 +71,7 @@
/**
* Keeps a list of all users on the device for user switching.
*/
-public class UserSwitcherController {
+public class UserSwitcherController implements Dumpable {
private static final String TAG = "UserSwitcherController";
private static final boolean DEBUG = false;
@@ -549,6 +550,7 @@
};
};
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("UserSwitcherController state:");
pw.println(" mLastNonGuestUser=" + mLastNonGuestUser);
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 238407a..a46f018 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,17 +16,13 @@
package com.android.systemui.usb;
-import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.res.XmlResourceParser;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
@@ -45,13 +41,8 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
-import com.android.internal.util.XmlUtils;
-import android.hardware.usb.AccessoryFilter;
-import android.hardware.usb.DeviceFilter;
import com.android.systemui.R;
-import org.xmlpull.v1.XmlPullParser;
-
public class UsbPermissionActivity extends AlertActivity
implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
@@ -71,12 +62,13 @@
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- Intent intent = getIntent();
+ Intent intent = getIntent();
mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
mUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- mPackageName = intent.getStringExtra("package");
+ mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE);
+ boolean canBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false);
PackageManager packageManager = getPackageManager();
ApplicationInfo aInfo;
@@ -105,121 +97,27 @@
ap.mPositiveButtonListener = this;
ap.mNegativeButtonListener = this;
- try {
- PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName,
- PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
-
- if ((mDevice != null && canBeDefault(mDevice, packageInfo))
- || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) {
- // add "open when" checkbox
- LayoutInflater inflater = (LayoutInflater) getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
- mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
- if (mDevice == null) {
- mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
- mAccessory.getDescription()));
- } else {
- mAlwaysUse.setText(getString(R.string.always_use_device, appName,
- mDevice.getProductName()));
- }
- mAlwaysUse.setOnCheckedChangeListener(this);
-
- mClearDefaultHint = (TextView)ap.mView.findViewById(
- com.android.internal.R.id.clearDefaultHint);
- mClearDefaultHint.setVisibility(View.GONE);
+ if (canBeDefault && (mDevice != null || mAccessory != null)) {
+ // add "open when" checkbox
+ LayoutInflater inflater = (LayoutInflater) getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+ if (mDevice == null) {
+ mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+ mAccessory.getDescription()));
+ } else {
+ mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+ mDevice.getProductName()));
}
- } catch (PackageManager.NameNotFoundException e) {
- // ignore
+ mAlwaysUse.setOnCheckedChangeListener(this);
+
+ mClearDefaultHint = (TextView)ap.mView.findViewById(
+ com.android.internal.R.id.clearDefaultHint);
+ mClearDefaultHint.setVisibility(View.GONE);
}
setupAlert();
-
- }
-
- /**
- * Can the app be the default for the USB device. I.e. can the app be launched by default if
- * the device is plugged in.
- *
- * @param device The device the app would be default for
- * @param packageInfo The package info of the app
- *
- * @return {@code true} iff the app can be default
- */
- private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) {
- ActivityInfo[] activities = packageInfo.activities;
- if (activities != null) {
- int numActivities = activities.length;
- for (int i = 0; i < numActivities; i++) {
- ActivityInfo activityInfo = activities[i];
-
- try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
- UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
- if (parser == null) {
- continue;
- }
-
- XmlUtils.nextElement(parser);
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- if ("usb-device".equals(parser.getName())) {
- DeviceFilter filter = DeviceFilter.read(parser);
- if (filter.matches(device)) {
- return true;
- }
- }
-
- XmlUtils.nextElement(parser);
- }
- } catch (Exception e) {
- Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
- }
- }
- }
-
- return false;
- }
-
- /**
- * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
- * the accessory is plugged in.
- *
- * @param accessory The accessory the app would be default for
- * @param packageInfo The package info of the app
- *
- * @return {@code true} iff the app can be default
- */
- private boolean canBeDefault(@NonNull UsbAccessory accessory,
- @NonNull PackageInfo packageInfo) {
- ActivityInfo[] activities = packageInfo.activities;
- if (activities != null) {
- int numActivities = activities.length;
- for (int i = 0; i < numActivities; i++) {
- ActivityInfo activityInfo = activities[i];
-
- try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
- UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
- if (parser == null) {
- continue;
- }
-
- XmlUtils.nextElement(parser);
- while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
- if ("usb-accessory".equals(parser.getName())) {
- AccessoryFilter filter = AccessoryFilter.read(parser);
- if (filter.matches(accessory)) {
- return true;
- }
- }
-
- XmlUtils.nextElement(parser);
- }
- } catch (Exception e) {
- Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
- }
- }
- }
-
- return false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java b/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java
index f7f61af..a791376 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java
@@ -44,7 +44,15 @@
mHandler = handler;
}
- public void schedule(long timeout, int mode) {
+ /**
+ * Schedules an alarm in {@code timeout} milliseconds in the future.
+ *
+ * @param timeout How long to wait from now.
+ * @param mode {@link #MODE_CRASH_IF_SCHEDULED}, {@link #MODE_IGNORE_IF_SCHEDULED} or
+ * {@link #MODE_RESCHEDULE_IF_SCHEDULED}.
+ * @return {@code true} when scheduled successfully, {@code false} otherwise.
+ */
+ public boolean schedule(long timeout, int mode) {
switch (mode) {
case MODE_CRASH_IF_SCHEDULED:
if (mScheduled) {
@@ -53,7 +61,7 @@
break;
case MODE_IGNORE_IF_SCHEDULED:
if (mScheduled) {
- return;
+ return false;
}
break;
case MODE_RESCHEDULE_IF_SCHEDULED:
@@ -68,6 +76,7 @@
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + timeout, mTag, this, mHandler);
mScheduled = true;
+ return true;
}
public boolean isScheduled() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java b/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java
new file mode 100644
index 0000000..d101ccb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/EmergencyDialerConstants.java
@@ -0,0 +1,38 @@
+/*
+ * 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.systemui.util;
+
+/**
+ * Constants defined and used in emergency dialer.
+ * Please keep these constants being consistent with those in com.android.phone.EmergencyDialer.
+ */
+public class EmergencyDialerConstants {
+ // Intent action for emergency dialer activity.
+ public static final String ACTION_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+
+ /**
+ * Extra included in {@link #ACTION_DIAL} to indicate the entry type that user starts
+ * the emergency dialer.
+ */
+ public static final String EXTRA_ENTRY_TYPE =
+ "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE";
+
+ // Indicating the entrance to emergency dialer
+ public static final int ENTRY_TYPE_UNKNOWN = 0;
+ public static final int ENTRY_TYPE_LOCKSCREEN_BUTTON = 1;
+ public static final int ENTRY_TYPE_POWER_MENU = 2;
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index f9f4f497..c5e4043 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -59,14 +59,14 @@
// No current icon, only the static drawable should be used.
s.icon = mock(Icon.class);
when(iv.getDrawable()).thenReturn(null);
- mIconView.updateIcon(iv, s);
+ mIconView.updateIcon(iv, s, true);
verify(s.icon, never()).getDrawable(any());
verify(s.icon).getInvisibleDrawable(any());
// Has icon, should use the standard (animated) form.
s.icon = mock(Icon.class);
when(iv.getDrawable()).thenReturn(mock(Drawable.class));
- mIconView.updateIcon(iv, s);
+ mIconView.updateIcon(iv, s, true);
verify(s.icon).getDrawable(any());
verify(s.icon, never()).getInvisibleDrawable(any());
}
@@ -79,7 +79,7 @@
int desiredColor = mIconView.getColor(s.state);
when(iv.isShown()).thenReturn(true);
- mIconView.setIcon(iv, s);
+ mIconView.setIcon(iv, s, true);
verify(iv).setImageTintList(argThat(stateList -> stateList.getColors()[0] == desiredColor));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index f04a115..f21ce27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -34,7 +34,6 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -91,7 +90,7 @@
return new TestableAlertingNotificationManager();
}
- private StatusBarNotification createNewNotification(int id) {
+ protected StatusBarNotification createNewNotification(int id) {
Notification.Builder n = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
@@ -154,7 +153,7 @@
public void testRemoveNotification_forceRemove() {
mAlertingNotificationManager.showNotification(mEntry);
- //Remove forcibly with releaseImmediately = true.
+ // Remove forcibly with releaseImmediately = true.
mAlertingNotificationManager.removeNotification(mEntry.key, true /* releaseImmediately */);
assertFalse(mAlertingNotificationManager.contains(mEntry.key));
@@ -173,4 +172,30 @@
assertEquals(0, mAlertingNotificationManager.getAllEntries().count());
}
+
+ @Test
+ public void testShouldExtendLifetime_notShownLongEnough() {
+ mAlertingNotificationManager.showNotification(mEntry);
+
+ // The entry has just been added so the lifetime should be extended
+ assertTrue(mAlertingNotificationManager.shouldExtendLifetime(mEntry));
+ }
+
+ @Test
+ public void testSetShouldManageLifetime_setShouldManage() {
+ mAlertingNotificationManager.showNotification(mEntry);
+
+ mAlertingNotificationManager.setShouldManageLifetime(mEntry, true /* shouldManage */);
+
+ assertTrue(mAlertingNotificationManager.mExtendedLifetimeAlertEntries.contains(mEntry));
+ }
+
+ @Test
+ public void testSetShouldManageLifetime_setShouldNotManage() {
+ mAlertingNotificationManager.mExtendedLifetimeAlertEntries.add(mEntry);
+
+ mAlertingNotificationManager.setShouldManageLifetime(mEntry, false /* shouldManage */);
+
+ assertFalse(mAlertingNotificationManager.mExtendedLifetimeAlertEntries.contains(mEntry));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 8129b01..09c1931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -86,8 +86,8 @@
entryManager.setUpWithPresenter(mPresenter, mListContainer, mEntryManagerCallback,
mHeadsUpManager);
- gutsManager.setUpWithPresenter(mPresenter, entryManager, mListContainer,
- mCheckSaveListener, mOnClickListener);
+ gutsManager.setUpWithPresenter(mPresenter, mListContainer, mCheckSaveListener,
+ mOnClickListener);
notificationLogger.setUpWithEntryManager(entryManager, mListContainer);
mediaManager.setUpWithPresenter(mPresenter, entryManager);
remoteInputManager.setUpWithPresenter(mPresenter, entryManager, mRemoteInputManagerCallback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 3cafaf4..7b0c0a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -85,13 +85,6 @@
}
@Test
- public void testPostNotificationRemovesKeyKeptForRemoteInput() {
- mListener.onNotificationPosted(mSbn, mRanking);
- TestableLooper.get(this).processAllMessages();
- verify(mEntryManager).removeKeyKeptForRemoteInput(mSbn.getKey());
- }
-
- @Test
public void testNotificationUpdateCallsUpdateNotification() {
when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn));
mListener.onNotificationPosted(mSbn, mRanking);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index afe2cf6..8a59e96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -1,8 +1,10 @@
package com.android.systemui.statusbar;
-import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertFalse;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -10,6 +12,7 @@
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -22,6 +25,10 @@
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputActiveExtender;
+import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputHistoryExtender;
+import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyHistoryExtender;
+
import com.google.android.collect.Sets;
import org.junit.Before;
@@ -41,6 +48,7 @@
@Mock private RemoteInputController.Delegate mDelegate;
@Mock private NotificationRemoteInputManager.Callback mCallback;
@Mock private RemoteInputController mController;
+ @Mock private SmartReplyController mSmartReplyController;
@Mock private NotificationListenerService.RankingMap mRanking;
@Mock private ExpandableNotificationRow mRow;
@@ -51,6 +59,9 @@
private TestableNotificationRemoteInputManager mRemoteInputManager;
private StatusBarNotification mSbn;
private NotificationData.Entry mEntry;
+ private RemoteInputHistoryExtender mRemoteInputHistoryExtender;
+ private SmartReplyHistoryExtender mSmartReplyHistoryExtender;
+ private RemoteInputActiveExtender mRemoteInputActiveExtender;
@Before
public void setUp() {
@@ -58,9 +69,9 @@
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
mLockscreenUserManager);
+ mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
when(mPresenter.getHandler()).thenReturn(Handler.createAsync(Looper.myLooper()));
- when(mEntryManager.getLatestRankingMap()).thenReturn(mRanking);
mRemoteInputManager = new TestableNotificationRemoteInputManager(mContext);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
@@ -70,20 +81,10 @@
mRemoteInputManager.setUpWithPresenterForTest(mPresenter, mEntryManager, mCallback,
mDelegate, mController);
- }
-
- @Test
- public void testOnRemoveNotificationNotKept() {
- assertFalse(mRemoteInputManager.onRemoveNotification(mEntry));
- assertTrue(mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().isEmpty());
- }
-
- @Test
- public void testOnRemoveNotificationKept() {
- when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
- assertTrue(mRemoteInputManager.onRemoveNotification(mEntry));
- assertTrue(mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().equals(
- Sets.newArraySet(mEntry)));
+ for (NotificationLifetimeExtender extender : mRemoteInputManager.getLifetimeExtenders()) {
+ extender.setCallback(
+ mock(NotificationLifetimeExtender.NotificationSafeToRemoveCallback.class));
+ }
}
@Test
@@ -95,15 +96,104 @@
}
@Test
- public void testRemoveRemoteInputEntriesKeptUntilCollapsed() {
- mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().add(mEntry);
- mRemoteInputManager.removeRemoteInputEntriesKeptUntilCollapsed();
+ public void testShouldExtendLifetime_remoteInputActive() {
+ when(mController.isRemoteInputActive(mEntry)).thenReturn(true);
- assertTrue(mRemoteInputManager.getRemoteInputEntriesToRemoveOnCollapse().isEmpty());
- verify(mController).removeRemoteInput(mEntry, null);
- verify(mEntryManager).removeNotification(mEntry.key, mRanking);
+ assertTrue(mRemoteInputActiveExtender.shouldExtendLifetime(mEntry));
}
+ @Test
+ public void testShouldExtendLifetime_isSpinning() {
+ NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY = true;
+ when(mController.isSpinning(mEntry.key)).thenReturn(true);
+
+ assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry));
+ }
+
+ @Test
+ public void testShouldExtendLifetime_recentRemoteInput() {
+ NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY = true;
+ mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
+
+ assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry));
+ }
+
+ @Test
+ public void testShouldExtendLifetime_smartReplySending() {
+ NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY = true;
+ when(mSmartReplyController.isSendingSmartReply(mEntry.key)).thenReturn(true);
+
+ assertTrue(mSmartReplyHistoryExtender.shouldExtendLifetime(mEntry));
+ }
+
+ @Test
+ public void testNotificationWithRemoteInputActiveIsRemovedOnCollapse() {
+ mRemoteInputActiveExtender.setShouldManageLifetime(mEntry, true /* shouldManage */);
+
+ assertEquals(mRemoteInputManager.getEntriesKeptForRemoteInputActive(),
+ Sets.newArraySet(mEntry));
+
+ mRemoteInputManager.onPanelCollapsed();
+
+ assertTrue(mRemoteInputManager.getEntriesKeptForRemoteInputActive().isEmpty());
+ }
+
+ @Test
+ public void testRebuildWithRemoteInput_noExistingInputNoSpinner() {
+ StatusBarNotification newSbn =
+ mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
+ CharSequence[] messages = newSbn.getNotification().extras
+ .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ assertEquals(1, messages.length);
+ assertEquals("A Reply", messages[0]);
+ assertFalse(newSbn.getNotification().extras
+ .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
+ assertTrue(newSbn.getNotification().extras
+ .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
+ }
+
+ @Test
+ public void testRebuildWithRemoteInput_noExistingInputWithSpinner() {
+ StatusBarNotification newSbn =
+ mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", true);
+ CharSequence[] messages = newSbn.getNotification().extras
+ .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ assertEquals(1, messages.length);
+ assertEquals("A Reply", messages[0]);
+ assertTrue(newSbn.getNotification().extras
+ .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
+ assertTrue(newSbn.getNotification().extras
+ .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
+ }
+
+ @Test
+ public void testRebuildWithRemoteInput_withExistingInput() {
+ // Setup a notification entry with 1 remote input.
+ StatusBarNotification newSbn =
+ mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
+ NotificationData.Entry entry = new NotificationData.Entry(newSbn);
+
+ // Try rebuilding to add another reply.
+ newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
+ CharSequence[] messages = newSbn.getNotification().extras
+ .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ assertEquals(2, messages.length);
+ assertEquals("Reply 2", messages[0]);
+ assertEquals("A Reply", messages[1]);
+ }
+
+ @Test
+ public void testRebuildNotificationForCanceledSmartReplies() {
+ // Try rebuilding to remove spinner and hide buttons.
+ StatusBarNotification newSbn =
+ mRemoteInputManager.rebuildNotificationForCanceledSmartReplies(mEntry);
+ assertFalse(newSbn.getNotification().extras
+ .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
+ assertTrue(newSbn.getNotification().extras
+ .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
+ }
+
+
private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {
public TestableNotificationRemoteInputManager(Context context) {
@@ -118,5 +208,15 @@
super.setUpWithPresenter(presenter, entryManager, callback, delegate);
mRemoteInputController = controller;
}
+
+ @Override
+ protected void addLifetimeExtenders() {
+ mRemoteInputActiveExtender = new RemoteInputActiveExtender();
+ mRemoteInputHistoryExtender = new RemoteInputHistoryExtender();
+ mSmartReplyHistoryExtender = new SmartReplyHistoryExtender();
+ mLifetimeExtenders.add(mRemoteInputHistoryExtender);
+ mLifetimeExtenders.add(mSmartReplyHistoryExtender);
+ mLifetimeExtenders.add(mRemoteInputActiveExtender);
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
index 42dad11..c2611e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java
@@ -92,15 +92,6 @@
}
@Test
- public void testOnDraw_ExcludeRectDrawable() {
- mView.setExcludedArea(new Rect(10, 10, 20, 20));
- Canvas canvas = mock(Canvas.class);
- mView.onDraw(canvas);
- // One time for each rect side
- verify(canvas, times(8)).clipRect(anyInt(), anyInt(), anyInt(), anyInt());
- }
-
- @Test
public void setTint_set() {
int tint = Color.BLUE;
mView.setTint(tint);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index ada5785..17daaac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -23,8 +23,11 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.Notification;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -35,6 +38,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Before;
import org.junit.Test;
@@ -46,97 +50,88 @@
@TestableLooper.RunWithLooper
@SmallTest
public class SmartReplyControllerTest extends SysuiTestCase {
- private static final String TEST_NOTIFICATION_KEY = "akey";
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
private static final String TEST_CHOICE_TEXT = "A Reply";
private static final int TEST_CHOICE_INDEX = 2;
private static final int TEST_CHOICE_COUNT = 4;
private Notification mNotification;
private NotificationData.Entry mEntry;
+ private SmartReplyController mSmartReplyController;
+ private NotificationRemoteInputManager mRemoteInputManager;
- @Mock
- private NotificationEntryManager mNotificationEntryManager;
- @Mock
- private IStatusBarService mIStatusBarService;
+ @Mock private NotificationPresenter mPresenter;
+ @Mock private RemoteInputController.Delegate mDelegate;
+ @Mock private NotificationRemoteInputManager.Callback mCallback;
+ @Mock private StatusBarNotification mSbn;
+ @Mock private NotificationEntryManager mNotificationEntryManager;
+ @Mock private IStatusBarService mIStatusBarService;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
mDependency.injectTestDependency(NotificationEntryManager.class,
mNotificationEntryManager);
mDependency.injectTestDependency(IStatusBarService.class, mIStatusBarService);
+ mSmartReplyController = new SmartReplyController();
+ mDependency.injectTestDependency(SmartReplyController.class,
+ mSmartReplyController);
+
+ mRemoteInputManager = new NotificationRemoteInputManager(mContext);
+ mRemoteInputManager.setUpWithPresenter(mPresenter, mNotificationEntryManager, mCallback,
+ mDelegate);
mNotification = new Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
.setContentTitle("Title")
.setContentText("Text").build();
- StatusBarNotification sbn = mock(StatusBarNotification.class);
- when(sbn.getNotification()).thenReturn(mNotification);
- when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
- mEntry = new NotificationData.Entry(sbn);
+
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
+ 0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0);
+ mEntry = new NotificationData.Entry(mSbn);
}
@Test
public void testSendSmartReply_updatesRemoteInput() {
- StatusBarNotification sbn = mock(StatusBarNotification.class);
- when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
- when(mNotificationEntryManager.rebuildNotificationWithRemoteInput(
- argThat(entry -> entry.notification.getKey().equals(TEST_NOTIFICATION_KEY)),
- eq(TEST_CHOICE_TEXT), eq(true))).thenReturn(sbn);
-
- SmartReplyController controller = new SmartReplyController();
- controller.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
// Sending smart reply should make calls to NotificationEntryManager
// to update the notification with reply and spinner.
- verify(mNotificationEntryManager).rebuildNotificationWithRemoteInput(
- argThat(entry -> entry.notification.getKey().equals(TEST_NOTIFICATION_KEY)),
- eq(TEST_CHOICE_TEXT), eq(true));
verify(mNotificationEntryManager).updateNotification(
- argThat(sbn2 -> sbn2.getKey().equals(TEST_NOTIFICATION_KEY)), isNull());
+ argThat(sbn -> sbn.getKey().equals(mSbn.getKey())), isNull());
}
@Test
public void testSendSmartReply_logsToStatusBar() throws RemoteException {
- StatusBarNotification sbn = mock(StatusBarNotification.class);
- when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
- when(mNotificationEntryManager.rebuildNotificationWithRemoteInput(
- argThat(entry -> entry.notification.getKey().equals(TEST_NOTIFICATION_KEY)),
- eq(TEST_CHOICE_TEXT), eq(true))).thenReturn(sbn);
-
- SmartReplyController controller = new SmartReplyController();
- controller.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
// Check we log the result to the status bar service.
- verify(mIStatusBarService).onNotificationSmartReplySent(TEST_NOTIFICATION_KEY,
+ verify(mIStatusBarService).onNotificationSmartReplySent(mSbn.getKey(),
TEST_CHOICE_INDEX);
}
@Test
public void testShowSmartReply_logsToStatusBar() throws RemoteException {
- SmartReplyController controller = new SmartReplyController();
- controller.smartRepliesAdded(mEntry, TEST_CHOICE_COUNT);
+ mSmartReplyController.smartRepliesAdded(mEntry, TEST_CHOICE_COUNT);
// Check we log the result to the status bar service.
- verify(mIStatusBarService).onNotificationSmartRepliesAdded(TEST_NOTIFICATION_KEY,
+ verify(mIStatusBarService).onNotificationSmartRepliesAdded(mSbn.getKey(),
TEST_CHOICE_COUNT);
}
@Test
public void testSendSmartReply_reportsSending() {
- SmartReplyController controller = new SmartReplyController();
- controller.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
- assertTrue(controller.isSendingSmartReply(TEST_NOTIFICATION_KEY));
+ assertTrue(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
}
@Test
public void testSendingSmartReply_afterRemove_shouldReturnFalse() {
- SmartReplyController controller = new SmartReplyController();
- controller.isSendingSmartReply(TEST_NOTIFICATION_KEY);
- controller.stopSending(mEntry);
+ mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT);
+ mSmartReplyController.stopSending(mEntry);
- assertFalse(controller.isSendingSmartReply(TEST_NOTIFICATION_KEY));
+ assertFalse(mSmartReplyController.isSendingSmartReply(mSbn.getKey()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 6543bdb..4e16b7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -57,6 +57,7 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -84,7 +85,6 @@
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -143,6 +143,10 @@
public CountDownLatch getCountDownLatch() {
return mCountDownLatch;
}
+
+ public ArrayList<NotificationLifetimeExtender> getLifetimeExtenders() {
+ return mNotificationLifetimeExtenders;
+ }
}
private void setUserSentiment(String key, int sentiment) {
@@ -279,7 +283,6 @@
verify(mBarService, never()).onNotificationError(any(), any(), anyInt(), anyInt(), anyInt(),
any(), anyInt());
- verify(mRemoteInputManager).onUpdateNotification(mEntry);
verify(mPresenter).updateNotificationViews();
verify(mForegroundServiceController).updateNotification(eq(mSbn), anyInt());
verify(mCallback).onNotificationUpdated(mSbn);
@@ -301,8 +304,6 @@
any(), anyInt());
verify(mMediaManager).onNotificationRemoved(mSbn.getKey());
- verify(mRemoteInputManager).onRemoveNotification(mEntry);
- verify(mSmartReplyController).stopSending(mEntry);
verify(mForegroundServiceController).removeNotification(mSbn);
verify(mListContainer).cleanUpViewState(mRow);
verify(mPresenter).updateNotificationViews();
@@ -313,17 +314,23 @@
}
@Test
- public void testRemoveNotification_blockedBySendingSmartReply() throws Exception {
+ public void testRemoveNotification_blockedByLifetimeExtender() {
com.android.systemui.util.Assert.isNotMainThread();
+ NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
+ when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
+
+ ArrayList<NotificationLifetimeExtender> extenders = mEntryManager.getLifetimeExtenders();
+ extenders.clear();
+ extenders.add(extender);
+
mEntry.row = mRow;
mEntryManager.getNotificationData().add(mEntry);
- when(mSmartReplyController.isSendingSmartReply(mEntry.key)).thenReturn(true);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
- assertTrue(mEntryManager.isNotificationKeptForRemoteInput(mEntry.key));
+ verify(extender).setShouldManageLifetime(mEntry, true /* shouldManage */);
}
@Test
@@ -411,61 +418,6 @@
}
@Test
- public void testRebuildWithRemoteInput_noExistingInputNoSpinner() {
- StatusBarNotification newSbn =
- mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
- CharSequence[] messages = newSbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
- Assert.assertEquals(1, messages.length);
- Assert.assertEquals("A Reply", messages[0]);
- Assert.assertFalse(newSbn.getNotification().extras
- .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
- Assert.assertTrue(newSbn.getNotification().extras
- .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
- }
-
- @Test
- public void testRebuildWithRemoteInput_noExistingInputWithSpinner() {
- StatusBarNotification newSbn =
- mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", true);
- CharSequence[] messages = newSbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
- Assert.assertEquals(1, messages.length);
- Assert.assertEquals("A Reply", messages[0]);
- Assert.assertTrue(newSbn.getNotification().extras
- .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
- Assert.assertTrue(newSbn.getNotification().extras
- .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
- }
-
- @Test
- public void testRebuildWithRemoteInput_withExistingInput() {
- // Setup a notification entry with 1 remote input.
- StatusBarNotification newSbn =
- mEntryManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
- NotificationData.Entry entry = new NotificationData.Entry(newSbn);
-
- // Try rebuilding to add another reply.
- newSbn = mEntryManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
- CharSequence[] messages = newSbn.getNotification().extras
- .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
- Assert.assertEquals(2, messages.length);
- Assert.assertEquals("Reply 2", messages[0]);
- Assert.assertEquals("A Reply", messages[1]);
- }
-
- @Test
- public void testRebuildNotificationForCanceledSmartReplies() {
- // Try rebuilding to remove spinner and hide buttons.
- StatusBarNotification newSbn =
- mEntryManager.rebuildNotificationForCanceledSmartReplies(mEntry);
- Assert.assertFalse(newSbn.getNotification().extras
- .getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false));
- Assert.assertTrue(newSbn.getNotification().extras
- .getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false));
- }
-
- @Test
public void testUpdateNotificationRanking() {
when(mPresenter.isDeviceProvisioned()).thenReturn(true);
when(mPresenter.isNotificationForCurrentProfiles(any())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 676cb61..8edbf17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -22,6 +22,8 @@
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -30,6 +32,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.spy;
@@ -54,6 +57,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationTestHelper;
@@ -99,7 +103,7 @@
mHelper = new NotificationTestHelper(mContext);
mGutsManager = new NotificationGutsManager(mContext);
- mGutsManager.setUpWithPresenter(mPresenter, mEntryManager, mStackScroller,
+ mGutsManager.setUpWithPresenter(mPresenter, mStackScroller,
mCheckSaveListener, mOnSettingsClickListener);
}
@@ -346,6 +350,35 @@
eq(true) /* isUserSentimentNegative */);
}
+ @Test
+ public void testShouldExtendLifetime() {
+ NotificationGuts guts = new NotificationGuts(mContext);
+ ExpandableNotificationRow row = spy(createTestNotificationRow());
+ doReturn(guts).when(row).getGuts();
+ NotificationData.Entry entry = row.getEntry();
+ entry.row = row;
+ mGutsManager.setExposedGuts(guts);
+
+ assertTrue(mGutsManager.shouldExtendLifetime(entry));
+ }
+
+ @Test
+ public void testSetShouldManageLifetime_setShouldManage() {
+ NotificationData.Entry entry = createTestNotificationRow().getEntry();
+ mGutsManager.setShouldManageLifetime(entry, true /* shouldManage */);
+
+ assertTrue(entry.key.equals(mGutsManager.mKeyToRemoveOnGutsClosed));
+ }
+
+ @Test
+ public void testSetShouldManageLifetime_setShouldNotManage() {
+ NotificationData.Entry entry = createTestNotificationRow().getEntry();
+ mGutsManager.mKeyToRemoveOnGutsClosed = entry.key;
+ mGutsManager.setShouldManageLifetime(entry, false /* shouldManage */);
+
+ assertNull(mGutsManager.mKeyToRemoveOnGutsClosed);
+ }
+
////////////////////////////////////////////////////////////////////////////////////////////////
// Utility methods:
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index c19188c..ce0bd58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -112,7 +112,8 @@
mEntryManager = new TestableNotificationEntryManager(mSystemServicesProxy, mPowerManager,
mContext);
- mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, null, null, mNotificationData);
+ mEntryManager.setUpForTest(mock(NotificationPresenter.class), null, null, mHeadsUpManager,
+ mNotificationData);
mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
NotificationShelf notificationShelf = spy(new NotificationShelf(getContext(), null));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index bdf7cd3..a81d17f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
+import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import org.junit.Before;
@@ -83,4 +84,26 @@
assertFalse(mHeadsUpManager.contains(mEntry.key));
}
+
+ @Test
+ public void testShouldExtendLifetime_swipedOut() {
+ mHeadsUpManager.showNotification(mEntry);
+ mHeadsUpManager.addSwipedOutNotification(mEntry.key);
+
+ // Notification is swiped so its lifetime should not be extended even if it hasn't been
+ // shown long enough
+ assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+ }
+
+ @Test
+ public void testShouldExtendLifetime_notTopEntry() {
+ NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
+ laterEntry.row = mRow;
+ mHeadsUpManager.showNotification(mEntry);
+ mHeadsUpManager.showNotification(laterEntry);
+
+ // Notification is "behind" a higher priority notification so we have no reason to keep
+ // its lifetime extended
+ assertFalse(mHeadsUpManager.shouldExtendLifetime(mEntry));
+ }
}
diff --git a/packages/VpnDialogs/res/values-ru/strings.xml b/packages/VpnDialogs/res/values-ru/strings.xml
index 0543937..f8fcfb8 100644
--- a/packages/VpnDialogs/res/values-ru/strings.xml
+++ b/packages/VpnDialogs/res/values-ru/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Запрос на подключение"</string>
- <string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. <br/><br/>Когда подключение к сети VPN активно, в верхней части экрана появляется значок <img src=vpn_icon />."</string>
+ <string name="warning" msgid="809658604548412033">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" пытается подключиться к сети VPN, чтобы отслеживать трафик. Этот запрос следует принимать, только если вы доверяете источнику. <br /><br />Когда подключение к сети VPN активно, в верхней части экрана появляется значок <img src=vpn_icon />."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN-подключение установлено"</string>
<string name="session" msgid="6470628549473641030">"Сеанс:"</string>
<string name="duration" msgid="3584782459928719435">"Продолжительность:"</string>
diff --git a/packages/VpnDialogs/res/values-vi/strings.xml b/packages/VpnDialogs/res/values-vi/strings.xml
index 097c9ae..fa5e114 100644
--- a/packages/VpnDialogs/res/values-vi/strings.xml
+++ b/packages/VpnDialogs/res/values-vi/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="prompt" msgid="3183836924226407828">"Yêu cầu kết nối"</string>
- <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. <br /> <br /> Biểu tượng <img src=vpn_icon /> xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
+ <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g> muốn thiết lập kết nối VPN cho phép ứng dụng giám sát lưu lượng truy cập mạng. Chỉ chấp nhận nếu bạn tin tưởng nguồn. Biểu tượng <br /> <br /> <img src=vpn_icon /> xuất hiện ở đầu màn hình của bạn khi VPN đang hoạt động."</string>
<string name="legacy_title" msgid="192936250066580964">"VPN được kết nối"</string>
<string name="session" msgid="6470628549473641030">"Phiên"</string>
<string name="duration" msgid="3584782459928719435">"Thời lượng:"</string>
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-bn/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-bn/strings.xml
new file mode 100644
index 0000000..41aa8045
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"কর্নার কাট-আউট"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-hi/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..435b664
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"कॉर्नर कटआउट"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-te/strings.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-te/strings.xml
new file mode 100644
index 0000000..4a31006
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="1677693377327336341">"మూల కట్అవుట్"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-bn/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-bn/strings.xml
new file mode 100644
index 0000000..d08874c
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"ডবল কাট-আউট"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-hi/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..d574f6c
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"डबल कटआउट"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-te/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-te/strings.xml
new file mode 100644
index 0000000..e32415c
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"డబుల్ కట్అవుట్"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-uz/strings.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-uz/strings.xml
index d585d7e..b307ac7 100644
--- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-uz/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-uz/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"Kesimni ikki marta kattalashtirish"</string>
+ <string name="display_cutout_emulation_overlay" msgid="5323179900047630217">"Kesimni ikkilantirish"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-bn/strings.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-bn/strings.xml
new file mode 100644
index 0000000..27f8290
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-bn/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3947428012427075896">"ন্যারো কাট-আউট"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-fr/strings.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-fr/strings.xml
index 551aa19..577a948 100644
--- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-fr/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-fr/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="3947428012427075896">"Encoche pour écran étroit"</string>
+ <string name="display_cutout_emulation_overlay" msgid="3947428012427075896">"Encoche étroite"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-hi/strings.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..39747d1
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-hi/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3947428012427075896">"नैराे कटआउट"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-te/strings.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-te/strings.xml
new file mode 100644
index 0000000..9bde419
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-te/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="3947428012427075896">"సన్నని కట్అవుట్"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-bn/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-bn/strings.xml
new file mode 100644
index 0000000..a2825df
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"টল কাট-আউট"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-fr/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-fr/strings.xml
index 58c42b4..5f4d729 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-fr/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-fr/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"Encoche pour écran haut"</string>
+ <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"Encoche haute"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-hi/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..a8bdf05
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"टॉल कटआउट"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-te/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-te/strings.xml
new file mode 100644
index 0000000..2e6233d
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"పొడవైన కట్అవుట్"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-uz/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-uz/strings.xml
index 5efafd5..f2e080a 100644
--- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-uz/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-uz/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"Kesimni balandroq qilish"</string>
+ <string name="display_cutout_emulation_overlay" msgid="6424539415439220018">"Kesimni balandlatish"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-bn/strings.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-bn/strings.xml
new file mode 100644
index 0000000..4492290
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-bn/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="4043478945358357737">"ওয়াইড কাট-আউট"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-fr/strings.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-fr/strings.xml
index ce8e131..f9b15cf 100644
--- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-fr/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-fr/strings.xml
@@ -17,5 +17,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="display_cutout_emulation_overlay" msgid="4043478945358357737">"Encoche pour écran large"</string>
+ <string name="display_cutout_emulation_overlay" msgid="4043478945358357737">"Encoche large"</string>
</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-hi/strings.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-hi/strings.xml
new file mode 100644
index 0000000..8f97495
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-hi/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="4043478945358357737">"वाइड कटआउट"</string>
+</resources>
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-te/strings.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-te/strings.xml
new file mode 100644
index 0000000..4da4f95
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-te/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="display_cutout_emulation_overlay" msgid="4043478945358357737">"వెడల్పైన కట్అవుట్"</string>
+</resources>
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b668623..41b3feb 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -6508,6 +6508,15 @@
// OS: Q
ACTION_EMERGENCY_DIALER_FROM_POWER_MENU = 1569;
+ // OPEN: Settings > System > Input & Gesture > Wake screen
+ // OS: Q
+ SETTINGS_GESTURE_WAKE_SCREEN = 1570;
+
+ // OPEN: Settings > Network & internet > Mobile network
+ // CATEGORY: SETTINGS
+ // OS: Q
+ MOBILE_NETWORK = 1571;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/accessibility/TEST_MAPPING b/services/accessibility/TEST_MAPPING
new file mode 100644
index 0000000..320924c
--- /dev/null
+++ b/services/accessibility/TEST_MAPPING
@@ -0,0 +1,105 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsAccessibilityServiceTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsAccessibilityTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsUiAutomationTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.accessibility"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "com.android.internal.accessibility"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.accessibility"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsAccessibilityServiceTestCases"
+ },
+ {
+ "name": "CtsAccessibilityTestCases"
+ },
+ {
+ "name": "CtsUiAutomationTestCases"
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.accessibility"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "com.android.internal.accessibility"
+ }
+ ]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.accessibility"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 6be9550..b2e06f9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -25,7 +25,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_WITH_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
@@ -3796,14 +3796,14 @@
*/
public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
- && (newMode != SHOW_MODE_WITH_HARD_KEYBOARD))
+ && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD))
{
Slog.w(LOG_TAG, "Invalid soft keyboard mode");
return false;
}
if (mSoftKeyboardShowMode == newMode) return true;
- if (newMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
if (hasUserOverriddenHardKeyboardSettingLocked()) {
// The user has specified a default for this setting
return false;
@@ -3811,13 +3811,13 @@
// Save the original value. But don't do this if the value in settings is already
// the new mode. That happens when we start up after a reboot, and we don't want
// to overwrite the value we had from when we first started controlling the setting.
- if (getSoftKeyboardValueFromSettings() != SHOW_MODE_WITH_HARD_KEYBOARD) {
+ if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
setOriginalHardKeyboardValue(
Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
}
putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
- } else if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
}
@@ -3837,7 +3837,7 @@
final ContentResolver cr = mContext.getContentResolver();
final boolean showWithHardKeyboardSettings =
Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
- if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
if (!showWithHardKeyboardSettings) {
// The user has overridden the setting. Respect that and prevent further changes
// to this behavior.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 8c8352f..c612c88 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -378,6 +378,7 @@
if (newSession == null) {
return NO_SESSION;
}
+ autofillId.setSessionId(newSession.id);
final String historyItem =
"id=" + newSession.id + " uid=" + uid + " a=" + shortComponentName
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5b7332d..a9257f6 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -23,6 +23,7 @@
import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
+import static android.view.autofill.AutofillManager.NO_SESSION;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.autofill.Helper.getNumericValue;
@@ -75,7 +76,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
@@ -128,7 +128,7 @@
private final MetricsLogger mMetricsLogger = new MetricsLogger();
- private static AtomicInteger sIdCounter = new AtomicInteger();
+ private static AtomicInteger sIdCounter = new AtomicInteger(1);
/** ID of the session */
public final int id;
@@ -385,6 +385,7 @@
@Override
public AutofillValue findRawValueByAutofillId(AutofillId id) {
+ if (id == null) return null;
synchronized (mLock) {
return findValueLocked(id);
}
@@ -395,16 +396,53 @@
* or {@code null} when not found on either of them.
*/
@GuardedBy("mLock")
- private AutofillValue findValueLocked(@NonNull AutofillId id) {
- final ViewState state = mViewStates.get(id);
+ @Nullable
+ private AutofillValue findValueLocked(@NonNull AutofillId autofillId) {
+ // TODO(b/113281366): rather than explicitly look for previous session, it might be
+ // better to merge the sessions when created (see note on mergePreviousSessionLocked())
+ final int requiredId = autofillId.getSessionId();
+ Session rightSession = null;
+ if (requiredId == NO_SESSION || requiredId == id) {
+ rightSession = this;
+ } else {
+ final ArrayList<Session> previousSessions = mService.getPreviousSessionsLocked(this);
+ if (previousSessions == null) {
+ if (sVerbose) Slog.v(TAG, "findValue(): no previous sessions");
+ return null;
+ }
+ if (sDebug) {
+ Slog.d(TAG, "findValue(): looking on " + previousSessions.size()
+ + " previous sessions for autofillId " + autofillId);
+ }
+ for (int i = 0; i < previousSessions.size(); i++) {
+ final Session previousSession = previousSessions.get(i);
+ if (previousSession.id == requiredId) {
+ rightSession = previousSession;
+ break;
+ }
+ }
+ }
+ if (rightSession == null) {
+ Slog.w(TAG, "findValue(): no session with id" + requiredId);
+ return null;
+ }
+ return rightSession.findValueFromThisSessionOnlyLocked(autofillId);
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private AutofillValue findValueFromThisSessionOnlyLocked(@NonNull AutofillId autofillId) {
+ final ViewState state = mViewStates.get(autofillId);
if (state == null) {
- if (sDebug) Slog.d(TAG, "findValueLocked(): no view state for " + id);
+ if (sDebug) {
+ Slog.d(TAG, "findValueLocked(): no view state for " + autofillId + " on " + id);
+ }
return null;
}
AutofillValue value = state.getCurrentValue();
if (value == null) {
- if (sDebug) Slog.d(TAG, "findValueLocked(): no current value for " + id);
- value = getValueFromContextsLocked(id);
+ Slog.d(TAG, "findValueLocked(): no current value for " + autofillId + " on " + id);
+ value = getValueFromContextsLocked(autofillId);
}
return value;
}
@@ -1769,15 +1807,16 @@
*/
@GuardedBy("mLock")
@Nullable
- private AutofillValue getValueFromContextsLocked(AutofillId id) {
+ private AutofillValue getValueFromContextsLocked(@NonNull AutofillId autofillId) {
final int numContexts = mContexts.size();
for (int i = numContexts - 1; i >= 0; i--) {
final FillContext context = mContexts.get(i);
- final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), id);
+ final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(),
+ autofillId);
if (node != null) {
final AutofillValue value = node.getAutofillValue();
if (sDebug) {
- Slog.d(TAG, "getValueFromContexts(" + id + ") at " + i + ": " + value);
+ Slog.d(TAG, "getValueFromContexts(" + autofillId + ") at " + i + ": " + value);
}
if (value != null && !value.isEmpty()) {
return value;
@@ -2954,11 +2993,13 @@
mMetricsLogger.write(newLogMaker(category));
}
+ @GuardedBy("mLock")
private void logAuthenticationStatusLocked(int requestId, int status) {
addTaggedDataToRequestLogLocked(requestId,
MetricsEvent.FIELD_AUTOFILL_AUTHENTICATION_STATUS, status);
}
+ @GuardedBy("mLock")
private void addTaggedDataToRequestLogLocked(int requestId, int tag, @Nullable Object value) {
final LogMaker requestLog = mRequestLogs.get(requestId);
if (requestLog == null) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index bb97e4a..e6cd7e0 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -212,20 +212,20 @@
public String toString() {
final StringBuilder builder = new StringBuilder("ViewState: [id=").append(id);
if (mDatasetId != null) {
- builder.append("datasetId:" ).append(mDatasetId);
+ builder.append(", datasetId:" ).append(mDatasetId);
}
builder.append("state:" ).append(getStateAsString());
if (mCurrentValue != null) {
- builder.append("currentValue:" ).append(mCurrentValue);
+ builder.append(", currentValue:" ).append(mCurrentValue);
}
if (mAutofilledValue != null) {
- builder.append("autofilledValue:" ).append(mAutofilledValue);
+ builder.append(", autofilledValue:" ).append(mAutofilledValue);
}
if (mSanitizedValue != null) {
- builder.append("sanitizedValue:" ).append(mSanitizedValue);
+ builder.append(", sanitizedValue:" ).append(mSanitizedValue);
}
if (mVirtualBounds != null) {
- builder.append("virtualBounds:" ).append(mVirtualBounds);
+ builder.append(", virtualBounds:" ).append(mVirtualBounds);
}
return builder.toString();
}
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 30ec8ab..125c225 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -9,9 +9,9 @@
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
-import android.app.backup.IBackupCallback;
import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
+import android.app.backup.IBackupCallback;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -21,6 +21,7 @@
import android.util.Slog;
import com.android.internal.util.Preconditions;
+import com.android.server.backup.fullbackup.AppMetadataBackupWriter;
import com.android.server.backup.remote.ServiceBackupCallback;
import com.android.server.backup.utils.FullBackupUtils;
@@ -202,16 +203,20 @@
public void run() {
try {
FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
+ AppMetadataBackupWriter writer =
+ new AppMetadataBackupWriter(output, mPackageManager);
if (DEBUG) {
Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
}
- FullBackupUtils.writeAppManifest(
- mPackage, mPackageManager, mManifestFile, false, false);
- FullBackup.backupToTar(mPackage.packageName, FullBackup.KEY_VALUE_DATA_TOKEN, null,
- mDataDir.getAbsolutePath(),
- mManifestFile.getAbsolutePath(),
- output);
+
+ writer.backupManifest(
+ mPackage,
+ mManifestFile,
+ mDataDir,
+ FullBackup.KEY_VALUE_DATA_TOKEN,
+ /* linkDomain */ null,
+ /* withApk */ false);
mManifestFile.delete();
if (DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
new file mode 100644
index 0000000..94365d7
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -0,0 +1,283 @@
+package com.android.server.backup.fullbackup;
+
+import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
+import static com.android.server.backup.BackupManagerService.TAG;
+
+import android.annotation.Nullable;
+import android.app.backup.FullBackup;
+import android.app.backup.FullBackupDataOutput;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.os.Build;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.StringBuilderPrinter;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Writes the backup of app-specific metadata to {@link FullBackupDataOutput}. This data is not
+ * backed up by the app's backup agent and is written before the agent writes its own data. This
+ * includes the app's:
+ *
+ * <ul>
+ * <li>manifest
+ * <li>widget data
+ * <li>apk
+ * <li>obb content
+ * </ul>
+ */
+// TODO(b/113807190): Fix or remove apk and obb implementation (only used for adb).
+public class AppMetadataBackupWriter {
+ private final FullBackupDataOutput mOutput;
+ private final PackageManager mPackageManager;
+
+ /** The destination of the backup is specified by {@code output}. */
+ public AppMetadataBackupWriter(FullBackupDataOutput output, PackageManager packageManager) {
+ mOutput = output;
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * Back up the app's manifest without specifying a pseudo-directory for the TAR stream.
+ *
+ * @see #backupManifest(PackageInfo, File, File, String, String, boolean)
+ */
+ public void backupManifest(
+ PackageInfo packageInfo, File manifestFile, File filesDir, boolean withApk)
+ throws IOException {
+ backupManifest(
+ packageInfo,
+ manifestFile,
+ filesDir,
+ /* domain */ null,
+ /* linkDomain */ null,
+ withApk);
+ }
+
+ /**
+ * Back up the app's manifest.
+ *
+ * <ol>
+ * <li>Write the app's manifest data to the specified temporary file {@code manifestFile}.
+ * <li>Backup the file in TAR format to the backup destination {@link #mOutput}.
+ * </ol>
+ *
+ * <p>Note: {@code domain} and {@code linkDomain} are only used by adb to specify a
+ * pseudo-directory for the TAR stream.
+ */
+ // TODO(b/113806991): Look into streaming the backup data directly.
+ public void backupManifest(
+ PackageInfo packageInfo,
+ File manifestFile,
+ File filesDir,
+ @Nullable String domain,
+ @Nullable String linkDomain,
+ boolean withApk)
+ throws IOException {
+ byte[] manifestBytes = getManifestBytes(packageInfo, withApk);
+ FileOutputStream outputStream = new FileOutputStream(manifestFile);
+ outputStream.write(manifestBytes);
+ outputStream.close();
+
+ // We want the manifest block in the archive stream to be constant each time we generate
+ // a backup stream for the app. However, the underlying TAR mechanism sees it as a file and
+ // will propagate its last modified time. We pin the last modified time to zero to prevent
+ // the TAR header from varying.
+ manifestFile.setLastModified(0);
+
+ FullBackup.backupToTar(
+ packageInfo.packageName,
+ domain,
+ linkDomain,
+ filesDir.getAbsolutePath(),
+ manifestFile.getAbsolutePath(),
+ mOutput);
+ }
+
+ /**
+ * Gets the app's manifest as a byte array. All data are strings ending in LF.
+ *
+ * <p>The manifest format is:
+ *
+ * <pre>
+ * BACKUP_MANIFEST_VERSION
+ * package name
+ * package version code
+ * platform version code
+ * installer package name (can be empty)
+ * boolean (1 if archive includes .apk, otherwise 0)
+ * # of signatures N
+ * N* (signature byte array in ascii format per Signature.toCharsString())
+ * </pre>
+ */
+ private byte[] getManifestBytes(PackageInfo packageInfo, boolean withApk) {
+ String packageName = packageInfo.packageName;
+ StringBuilder builder = new StringBuilder(4096);
+ StringBuilderPrinter printer = new StringBuilderPrinter(builder);
+
+ printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
+ printer.println(packageName);
+ printer.println(Long.toString(packageInfo.getLongVersionCode()));
+ printer.println(Integer.toString(Build.VERSION.SDK_INT));
+
+ String installerName = mPackageManager.getInstallerPackageName(packageName);
+ printer.println((installerName != null) ? installerName : "");
+
+ printer.println(withApk ? "1" : "0");
+
+ // Write the signature block.
+ SigningInfo signingInfo = packageInfo.signingInfo;
+ if (signingInfo == null) {
+ printer.println("0");
+ } else {
+ // Retrieve the newest signatures to write.
+ // TODO (b/73988180) use entire signing history in case of rollbacks.
+ Signature[] signatures = signingInfo.getApkContentsSigners();
+ printer.println(Integer.toString(signatures.length));
+ for (Signature sig : signatures) {
+ printer.println(sig.toCharsString());
+ }
+ }
+ return builder.toString().getBytes();
+ }
+
+ /**
+ * Backup specified widget data. The widget data is prefaced by a metadata header.
+ *
+ * <ol>
+ * <li>Write a metadata header to the specified temporary file {@code metadataFile}.
+ * <li>Write widget data bytes to the same file.
+ * <li>Backup the file in TAR format to the backup destination {@link #mOutput}.
+ * </ol>
+ *
+ * @throws IllegalArgumentException if the widget data provided is empty.
+ */
+ // TODO(b/113806991): Look into streaming the backup data directly.
+ public void backupWidget(
+ PackageInfo packageInfo, File metadataFile, File filesDir, byte[] widgetData)
+ throws IOException {
+ Preconditions.checkArgument(widgetData.length > 0, "Can't backup widget with no data.");
+
+ String packageName = packageInfo.packageName;
+ FileOutputStream fileOutputStream = new FileOutputStream(metadataFile);
+ BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
+ DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);
+
+ byte[] metadata = getMetadataBytes(packageName);
+ bufferedOutputStream.write(metadata); // bypassing DataOutputStream
+ writeWidgetData(dataOutputStream, widgetData);
+ bufferedOutputStream.flush();
+ dataOutputStream.close();
+
+ // As with the manifest file, guarantee consistency of the archive metadata for the widget
+ // block by using a fixed last modified time on the metadata file.
+ metadataFile.setLastModified(0);
+
+ FullBackup.backupToTar(
+ packageName,
+ /* domain */ null,
+ /* linkDomain */ null,
+ filesDir.getAbsolutePath(),
+ metadataFile.getAbsolutePath(),
+ mOutput);
+ }
+
+ /**
+ * Gets the app's metadata as a byte array. All entries are strings ending in LF.
+ *
+ * <p>The metadata format is:
+ *
+ * <pre>
+ * BACKUP_METADATA_VERSION
+ * package name
+ * </pre>
+ */
+ private byte[] getMetadataBytes(String packageName) {
+ StringBuilder builder = new StringBuilder(512);
+ StringBuilderPrinter printer = new StringBuilderPrinter(builder);
+ printer.println(Integer.toString(BACKUP_METADATA_VERSION));
+ printer.println(packageName);
+ return builder.toString().getBytes();
+ }
+
+ /**
+ * Write a byte array of widget data to the specified output stream. All integers are binary in
+ * network byte order.
+ *
+ * <p>The widget data format:
+ *
+ * <pre>
+ * 4 : Integer token identifying the widget data blob.
+ * 4 : Integer size of the widget data.
+ * N : Raw bytes of the widget data.
+ * </pre>
+ */
+ private void writeWidgetData(DataOutputStream out, byte[] widgetData) throws IOException {
+ out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
+ out.writeInt(widgetData.length);
+ out.write(widgetData);
+ }
+
+ /**
+ * Backup the app's .apk to the backup destination {@link #mOutput}. Currently only used for
+ * 'adb backup'.
+ */
+ // TODO(b/113807190): Investigate and potentially remove.
+ public void backupApk(PackageInfo packageInfo) {
+ // TODO: handle backing up split APKs
+ String appSourceDir = packageInfo.applicationInfo.getBaseCodePath();
+ String apkDir = new File(appSourceDir).getParent();
+ FullBackup.backupToTar(
+ packageInfo.packageName,
+ FullBackup.APK_TREE_TOKEN,
+ /* linkDomain */ null,
+ apkDir,
+ appSourceDir,
+ mOutput);
+ }
+
+ /**
+ * Backup the app's .obb files to the backup destination {@link #mOutput}. Currently only used
+ * for 'adb backup'.
+ */
+ // TODO(b/113807190): Investigate and potentially remove.
+ public void backupObb(PackageInfo packageInfo) {
+ // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM doesn't have access to
+ // external storage.
+ // TODO: http://b/22388012
+ Environment.UserEnvironment userEnv =
+ new Environment.UserEnvironment(UserHandle.USER_SYSTEM);
+ File obbDir = userEnv.buildExternalStorageAppObbDirs(packageInfo.packageName)[0];
+ if (obbDir != null) {
+ if (MORE_DEBUG) {
+ Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
+ }
+ File[] obbFiles = obbDir.listFiles();
+ if (obbFiles != null) {
+ String obbDirName = obbDir.getAbsolutePath();
+ for (File obb : obbFiles) {
+ FullBackup.backupToTar(
+ packageInfo.packageName,
+ FullBackup.OBB_TREE_TOKEN,
+ /* linkDomain */ null,
+ obbDirName,
+ obb.getAbsolutePath(),
+ mOutput);
+ }
+ }
+ }
+ }
+}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 16906f7..c9f7218 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -18,8 +18,6 @@
import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
-import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
@@ -29,17 +27,14 @@
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
import android.app.backup.BackupTransport;
-import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.os.Environment.UserEnvironment;
+import android.content.pm.PackageManager;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.util.Log;
import android.util.Slog;
-import android.util.StringBuilderPrinter;
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
@@ -49,27 +44,20 @@
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.utils.FullBackupUtils;
-import java.io.BufferedOutputStream;
-import java.io.DataOutputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
- * Core logic for performing one package's full backup, gathering the tarball from the
- * application and emitting it to the designated OutputStream.
+ * Core logic for performing one package's full backup, gathering the tarball from the application
+ * and emitting it to the designated OutputStream.
*/
public class FullBackupEngine {
-
private BackupManagerService backupManagerService;
OutputStream mOutput;
FullBackupPreflight mPreflightHook;
BackupRestoreTask mTimeoutMonitor;
IBackupAgent mAgent;
- File mFilesDir;
- File mManifestFile;
- File mMetadataFile;
boolean mIncludeApks;
PackageInfo mPkg;
private final long mQuota;
@@ -78,79 +66,91 @@
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
class FullBackupRunner implements Runnable {
+ private final PackageManager mPackageManager;
+ private final PackageInfo mPackage;
+ private final IBackupAgent mAgent;
+ private final ParcelFileDescriptor mPipe;
+ private final int mToken;
+ private final boolean mIncludeApks;
+ private final File mFilesDir;
- PackageInfo mPackage;
- byte[] mWidgetData;
- IBackupAgent mAgent;
- ParcelFileDescriptor mPipe;
- int mToken;
- boolean mSendApk;
- boolean mWriteManifest;
-
- FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
- int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
+ FullBackupRunner(
+ PackageInfo packageInfo,
+ IBackupAgent agent,
+ ParcelFileDescriptor pipe,
+ int token,
+ boolean includeApks)
throws IOException {
- mPackage = pack;
- mWidgetData = widgetData;
+ mPackageManager = backupManagerService.getPackageManager();
+ mPackage = packageInfo;
mAgent = agent;
mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
mToken = token;
- mSendApk = sendApk;
- mWriteManifest = writeManifest;
+ mIncludeApks = includeApks;
+ mFilesDir = new File("/data/system");
}
@Override
public void run() {
try {
- FullBackupDataOutput output = new FullBackupDataOutput(
- mPipe, -1, mTransportFlags);
+ FullBackupDataOutput output =
+ new FullBackupDataOutput(mPipe, /* quota */ -1, mTransportFlags);
+ AppMetadataBackupWriter appMetadataBackupWriter =
+ new AppMetadataBackupWriter(output, mPackageManager);
- if (mWriteManifest) {
- final boolean writeWidgetData = mWidgetData != null;
+ String packageName = mPackage.packageName;
+ boolean isSharedStorage = SHARED_BACKUP_AGENT_PACKAGE.equals(packageName);
+ boolean writeApk =
+ shouldWriteApk(mPackage.applicationInfo, mIncludeApks, isSharedStorage);
+
+ if (!isSharedStorage) {
if (MORE_DEBUG) {
- Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
+ Slog.d(TAG, "Writing manifest for " + packageName);
}
- FullBackupUtils
- .writeAppManifest(mPackage, backupManagerService.getPackageManager(),
- mManifestFile, mSendApk,
- writeWidgetData);
- FullBackup.backupToTar(mPackage.packageName, null, null,
- mFilesDir.getAbsolutePath(),
- mManifestFile.getAbsolutePath(),
- output);
- mManifestFile.delete();
- // We only need to write a metadata file if we have widget data to stash
- if (writeWidgetData) {
- writeMetadata(mPackage, mMetadataFile, mWidgetData);
- FullBackup.backupToTar(mPackage.packageName, null, null,
- mFilesDir.getAbsolutePath(),
- mMetadataFile.getAbsolutePath(),
- output);
- mMetadataFile.delete();
+ File manifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
+ appMetadataBackupWriter.backupManifest(
+ mPackage, manifestFile, mFilesDir, writeApk);
+ manifestFile.delete();
+
+ // Write widget data.
+ // TODO: http://b/22388012
+ byte[] widgetData =
+ AppWidgetBackupBridge.getWidgetState(
+ packageName, UserHandle.USER_SYSTEM);
+ if (widgetData != null && widgetData.length > 0) {
+ File metadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
+ appMetadataBackupWriter.backupWidget(
+ mPackage, metadataFile, mFilesDir, widgetData);
+ metadataFile.delete();
}
}
- if (mSendApk) {
- writeApkToBackup(mPackage, output);
+ // TODO(b/113807190): Look into removing, only used for 'adb backup'.
+ if (writeApk) {
+ appMetadataBackupWriter.backupApk(mPackage);
+ appMetadataBackupWriter.backupObb(mPackage);
}
- final boolean isSharedStorage =
- mPackage.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
- final long timeout = isSharedStorage ?
- mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
- mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
-
if (DEBUG) {
- Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
+ Slog.d(TAG, "Calling doFullBackup() on " + packageName);
}
- backupManagerService
- .prepareOperationTimeout(mToken,
- timeout,
- mTimeoutMonitor /* in parent class */,
- OP_TYPE_BACKUP_WAIT);
- mAgent.doFullBackup(mPipe, mQuota, mToken,
- backupManagerService.getBackupManagerBinder(), mTransportFlags);
+
+ long timeout =
+ isSharedStorage
+ ? mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis()
+ : mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
+ backupManagerService.prepareOperationTimeout(
+ mToken,
+ timeout,
+ mTimeoutMonitor /* in parent class */,
+ OP_TYPE_BACKUP_WAIT);
+ mAgent.doFullBackup(
+ mPipe,
+ mQuota,
+ mToken,
+ backupManagerService.getBackupManagerBinder(),
+ mTransportFlags);
} catch (IOException e) {
Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
} catch (RemoteException e) {
@@ -162,12 +162,33 @@
}
}
}
+
+ /**
+ * Don't write apks for forward-locked apps or system-bundled apps that are not upgraded.
+ */
+ private boolean shouldWriteApk(
+ ApplicationInfo applicationInfo, boolean includeApks, boolean isSharedStorage) {
+ boolean isForwardLocked =
+ (applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
+ boolean isSystemApp = (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ boolean isUpdatedSystemApp =
+ (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ return includeApks
+ && !isSharedStorage
+ && !isForwardLocked
+ && (!isSystemApp || isUpdatedSystemApp);
+ }
}
- public FullBackupEngine(BackupManagerService backupManagerService,
+ public FullBackupEngine(
+ BackupManagerService backupManagerService,
OutputStream output,
- FullBackupPreflight preflightHook, PackageInfo pkg,
- boolean alsoApks, BackupRestoreTask timeoutMonitor, long quota, int opToken,
+ FullBackupPreflight preflightHook,
+ PackageInfo pkg,
+ boolean alsoApks,
+ BackupRestoreTask timeoutMonitor,
+ long quota,
+ int opToken,
int transportFlags) {
this.backupManagerService = backupManagerService;
mOutput = output;
@@ -175,15 +196,13 @@
mPkg = pkg;
mIncludeApks = alsoApks;
mTimeoutMonitor = timeoutMonitor;
- mFilesDir = new File("/data/system");
- mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
- mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
mQuota = quota;
mOpToken = opToken;
mTransportFlags = transportFlags;
- mAgentTimeoutParameters = Preconditions.checkNotNull(
- backupManagerService.getAgentTimeoutParameters(),
- "Timeout parameters cannot be null");
+ mAgentTimeoutParameters =
+ Preconditions.checkNotNull(
+ backupManagerService.getAgentTimeoutParameters(),
+ "Timeout parameters cannot be null");
}
public int preflightCheck() throws RemoteException {
@@ -213,27 +232,13 @@
try {
pipes = ParcelFileDescriptor.createPipe();
- ApplicationInfo app = mPkg.applicationInfo;
- final boolean isSharedStorage =
- mPkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
- final boolean sendApk = mIncludeApks
- && !isSharedStorage
- && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
- && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
- (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
-
- // TODO: http://b/22388012
- byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(mPkg.packageName,
- UserHandle.USER_SYSTEM);
-
- FullBackupRunner runner = new FullBackupRunner(mPkg, mAgent, pipes[1],
- mOpToken, sendApk, !isSharedStorage, widgetBlob);
- pipes[1].close(); // the runner has dup'd it
+ FullBackupRunner runner =
+ new FullBackupRunner(mPkg, mAgent, pipes[1], mOpToken, mIncludeApks);
+ pipes[1].close(); // the runner has dup'd it
pipes[1] = null;
Thread t = new Thread(runner, "app-data-runner");
t.start();
- // Now pull data from the app and stuff it into the output
FullBackupUtils.routeSocketDataToOutput(pipes[0], mOutput);
if (!backupManagerService.waitUntilOperationComplete(mOpToken)) {
@@ -288,84 +293,13 @@
if (MORE_DEBUG) {
Slog.d(TAG, "Binding to full backup agent : " + mPkg.packageName);
}
- mAgent = backupManagerService.bindToAgentSynchronous(mPkg.applicationInfo,
- ApplicationThreadConstants.BACKUP_MODE_FULL);
+ mAgent =
+ backupManagerService.bindToAgentSynchronous(
+ mPkg.applicationInfo, ApplicationThreadConstants.BACKUP_MODE_FULL);
}
return mAgent != null;
}
- private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
- // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
- // TODO: handle backing up split APKs
- final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
- final String apkDir = new File(appSourceDir).getParent();
- FullBackup.backupToTar(pkg.packageName, FullBackup.APK_TREE_TOKEN, null,
- apkDir, appSourceDir, output);
-
- // TODO: migrate this to SharedStorageBackup, since AID_SYSTEM
- // doesn't have access to external storage.
-
- // Save associated .obb content if it exists and we did save the apk
- // check for .obb and save those too
- // TODO: http://b/22388012
- final UserEnvironment userEnv = new UserEnvironment(UserHandle.USER_SYSTEM);
- final File obbDir = userEnv.buildExternalStorageAppObbDirs(pkg.packageName)[0];
- if (obbDir != null) {
- if (MORE_DEBUG) {
- Log.i(TAG, "obb dir: " + obbDir.getAbsolutePath());
- }
- File[] obbFiles = obbDir.listFiles();
- if (obbFiles != null) {
- final String obbDirName = obbDir.getAbsolutePath();
- for (File obb : obbFiles) {
- FullBackup.backupToTar(pkg.packageName, FullBackup.OBB_TREE_TOKEN, null,
- obbDirName, obb.getAbsolutePath(), output);
- }
- }
- }
- }
-
- // Widget metadata format. All header entries are strings ending in LF:
- //
- // Version 1 header:
- // BACKUP_METADATA_VERSION, currently "1"
- // package name
- //
- // File data (all integers are binary in network byte order)
- // *N: 4 : integer token identifying which metadata blob
- // 4 : integer size of this blob = N
- // N : raw bytes of this metadata blob
- //
- // Currently understood blobs (always in network byte order):
- //
- // widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
- //
- // Unrecognized blobs are *ignored*, not errors.
- private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
- throws IOException {
- StringBuilder b = new StringBuilder(512);
- StringBuilderPrinter printer = new StringBuilderPrinter(b);
- printer.println(Integer.toString(BACKUP_METADATA_VERSION));
- printer.println(pkg.packageName);
-
- FileOutputStream fout = new FileOutputStream(destination);
- BufferedOutputStream bout = new BufferedOutputStream(fout);
- DataOutputStream out = new DataOutputStream(bout);
- bout.write(b.toString().getBytes()); // bypassing DataOutputStream
-
- if (widgetData != null && widgetData.length > 0) {
- out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
- out.writeInt(widgetData.length);
- out.write(widgetData);
- }
- bout.flush();
- out.close();
-
- // As with the manifest file, guarantee idempotence of the archive metadata
- // for the widget block by using a fixed mtime on the transient file.
- destination.setLastModified(0);
- }
-
private void tearDown() {
if (mPkg != null) {
backupManagerService.tearDownAgentAndKill(mPkg.applicationInfo);
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index a4cd629..e915ce1 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -249,10 +249,10 @@
private final List<String> mPendingFullBackups;
private final Object mQueueLock;
@Nullable private final DataChangedJournal mJournal;
- @Nullable private PerformFullTransportBackupTask mFullBackupTask;
private int mStatus;
- @Nullable private IBackupAgent mAgentBinder;
+ @Nullable private PerformFullTransportBackupTask mFullBackupTask;
+ @Nullable private IBackupAgent mAgent;
@Nullable private PackageInfo mCurrentPackage;
@Nullable private File mSavedStateFile;
@Nullable private File mBackupDataFile;
@@ -349,24 +349,24 @@
// Not an explicit cancel, we need to flag it.
mCancelled = true;
mReporter.onAgentCancelled(packageInfo);
- errorCleanup();
+ cleanUpAgentForAgentError();
return false;
}
if (result == RemoteResult.FAILED_CANCELLED) {
mReporter.onAgentCancelled(packageInfo);
- errorCleanup();
+ cleanUpAgentForAgentError();
return false;
}
if (result == RemoteResult.FAILED_TIMED_OUT) {
mReporter.onAgentTimedOut(packageInfo);
- errorCleanup();
+ cleanUpAgentForAgentError();
return true;
}
Preconditions.checkState(result.isPresent());
long agentResult = result.get();
if (agentResult == BackupAgent.RESULT_ERROR) {
mReporter.onAgentResultError(packageInfo);
- errorCleanup();
+ cleanUpAgentForAgentError();
return true;
}
return sendDataToTransport();
@@ -380,37 +380,22 @@
/** Returns whether to consume next queue package. */
private boolean startTask() {
- synchronized (mBackupManagerService.getCurrentOpLock()) {
- if (mBackupManagerService.isBackupOperationInProgress()) {
- mReporter.onSkipBackup();
- return false;
- }
+ if (mBackupManagerService.isBackupOperationInProgress()) {
+ mReporter.onSkipBackup();
+ return false;
}
- String[] fullBackups = mPendingFullBackups.toArray(new String[mPendingFullBackups.size()]);
- mFullBackupTask =
- new PerformFullTransportBackupTask(
- mBackupManagerService,
- mTransportClient,
- /* fullBackupRestoreObserver */ null,
- fullBackups,
- /* updateSchedule */ false,
- /* runningJob */ null,
- new CountDownLatch(1),
- mReporter.getObserver(),
- mReporter.getMonitor(),
- mTaskFinishedListener,
- mUserInitiated);
+ // Unfortunately full backup task constructor registers the task with BMS, so we have to
+ // create it here instead of in our constructor.
+ mFullBackupTask = createFullBackupTask(mPendingFullBackups);
registerTask();
- mAgentBinder = null;
mStatus = BackupTransport.TRANSPORT_OK;
if (mQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
mReporter.onEmptyQueueAtStart();
return false;
}
-
// We only backup PM if it was explicitly in the queue or if it's incremental.
boolean backupPm = mQueue.remove(PM_PACKAGE) || !mNonIncremental;
if (backupPm) {
@@ -446,6 +431,21 @@
return true;
}
+ private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) {
+ return new PerformFullTransportBackupTask(
+ mBackupManagerService,
+ mTransportClient,
+ /* fullBackupRestoreObserver */ null,
+ packages.toArray(new String[packages.size()]),
+ /* updateSchedule */ false,
+ /* runningJob */ null,
+ new CountDownLatch(1),
+ mReporter.getObserver(),
+ mReporter.getMonitor(),
+ mTaskFinishedListener,
+ mUserInitiated);
+ }
+
/** Returns whether to consume next queue package. */
private boolean backupPm() {
RemoteResult agentResult = null;
@@ -455,10 +455,9 @@
// Since PM is running in the system process we can set up its agent directly.
BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
- Pair<Integer, RemoteResult> statusAndResult =
- extractAgentData(
- PM_PACKAGE,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+ mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind());
+
+ Pair<Integer, RemoteResult> statusAndResult = extractAgentData(PM_PACKAGE, mAgent);
mStatus = statusAndResult.first;
agentResult = statusAndResult.second;
} catch (Exception e) {
@@ -467,6 +466,8 @@
}
if (mStatus != BackupTransport.TRANSPORT_OK) {
+ // In this case either extractAgentData() already made the agent clean-up or we haven't
+ // prepared the state for calling the agent, in either case we don't need to clean-up.
mBackupManagerService.resetBackupState(mStateDirectory);
return false;
}
@@ -512,7 +513,7 @@
applicationInfo,
ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
if (agent != null) {
- mAgentBinder = agent;
+ mAgent = agent;
Pair<Integer, RemoteResult> statusAndResult =
extractAgentData(packageName, agent);
mStatus = statusAndResult.first;
@@ -532,7 +533,9 @@
}
if (mStatus != BackupTransport.TRANSPORT_OK) {
- mAgentBinder = null;
+ // In this case either extractAgentData() already made the agent clean-up or we haven't
+ // prepared the state for calling the agent, in either case we don't need to clean-up.
+ Preconditions.checkState(mAgent == null);
if (mStatus == BackupTransport.AGENT_ERROR) {
mReporter.onAgentError(packageName);
@@ -710,7 +713,7 @@
"doBackup()");
} catch (Exception e) {
mReporter.onCallAgentDoBackupError(packageName, callingAgent, e);
- errorCleanup();
+ cleanUpAgentForAgentError();
// TODO: Remove the check on callingAgent when RemoteCall supports local agent calls.
int status =
callingAgent ? BackupTransport.AGENT_ERROR : BackupTransport.TRANSPORT_ERROR;
@@ -808,7 +811,7 @@
boolean writingWidgetData = false;
try {
if (!validateBackupData(applicationInfo, mBackupDataFile)) {
- errorCleanup();
+ cleanUpAgentForAgentError();
return true;
}
writingWidgetData = true;
@@ -819,11 +822,11 @@
} else {
mReporter.onReadAgentDataError(packageName, e);
}
+ cleanUpAgentForAgentError();
revertTask();
return false;
}
- clearAgentState();
boolean nonIncremental = mSavedStateFile.length() == 0;
long size = mBackupDataFile.length();
if (size > 0) {
@@ -853,31 +856,12 @@
mStatus = BackupTransport.TRANSPORT_ERROR;
}
- updateFiles(mStatus);
- return handleTransportStatus(mStatus, packageName, size);
- }
- private void updateFiles(int status) {
- switch (status) {
- case BackupTransport.TRANSPORT_OK:
- mBackupDataFile.delete();
- mNewStateFile.renameTo(mSavedStateFile);
- break;
- case BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED:
- mSavedStateFile.delete();
- mBackupDataFile.delete();
- mNewStateFile.delete();
- break;
- default:
- // Includes:
- // * BackupTransport.TRANSPORT_PACKAGE_REJECTED
- // * BackupTransport.TRANSPORT_QUOTA_EXCEEDED
- // * BackupTransport.TRANSPORT_ERROR
- mBackupDataFile.delete();
- mNewStateFile.delete();
- break;
-
- }
+ boolean processQueue = handleTransportStatus(mStatus, packageName, size);
+ // We might report quota exceeded to the agent in handleTransportStatus() above, so we
+ // only clean-up after it.
+ cleanUpAgentForTransportStatus(mStatus);
+ return processQueue;
}
/** Returns whether to consume next queue package. */
@@ -898,7 +882,7 @@
}
if (status == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
mReporter.onPackageBackupQuotaExceeded(packageName);
- agentDoQuotaExceeded(mAgentBinder, packageName, size);
+ agentDoQuotaExceeded(mAgent, packageName, size);
return true;
}
// Any other error here indicates a transport-level failure.
@@ -943,7 +927,7 @@
if (key != null && key.charAt(0) >= 0xff00) {
mReporter.onAgentIllegalKey(mCurrentPackage, key);
// Crash them if they wrote any protected keys.
- agentFail(mAgentBinder, "Illegal backup key: " + key);
+ agentFail(mAgent, "Illegal backup key: " + key);
return false;
}
backupDataInput.skipEntityData();
@@ -1025,21 +1009,50 @@
}
}
- private void errorCleanup() {
- mBackupDataFile.delete();
- mNewStateFile.delete();
- clearAgentState();
+ /** Cleans-up after having called the agent. */
+ private void cleanUpAgentForTransportStatus(int status) {
+ updateFiles(status);
+ cleanUpAgent();
}
- private void clearAgentState() {
- // Cleanup common to both success and failure cases.
+ /** Cleans-up if we failed to call the agent. */
+ private void cleanUpAgentForAgentError() {
+ mBackupDataFile.delete();
+ mNewStateFile.delete();
+ cleanUpAgent();
+ }
+
+ private void updateFiles(int status) {
+ switch (status) {
+ case BackupTransport.TRANSPORT_OK:
+ mBackupDataFile.delete();
+ mNewStateFile.renameTo(mSavedStateFile);
+ break;
+ case BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED:
+ mSavedStateFile.delete();
+ mBackupDataFile.delete();
+ mNewStateFile.delete();
+ break;
+ default:
+ // Includes:
+ // * BackupTransport.TRANSPORT_PACKAGE_REJECTED
+ // * BackupTransport.TRANSPORT_QUOTA_EXCEEDED
+ // * BackupTransport.TRANSPORT_ERROR
+ mBackupDataFile.delete();
+ mNewStateFile.delete();
+ break;
+ }
+ }
+
+ /** Cleans-up file-descriptors and unbinds agent. */
+ private void cleanUpAgent() {
+ mAgent = null;
tryCloseFileDescriptor(mSavedState, "old state");
tryCloseFileDescriptor(mBackupData, "backup data");
tryCloseFileDescriptor(mNewState, "new state");
- synchronized (mBackupManagerService.getCurrentOpLock()) {
- // TODO: Do we still need this?
- mSavedState = mBackupData = mNewState = null;
- }
+ mSavedState = null;
+ mBackupData = null;
+ mNewState = null;
// For PM metadata (for which applicationInfo is null) there is no agent-bound state.
if (mCurrentPackage.applicationInfo != null) {
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
index a3d5601..dbe3cd9 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -16,23 +16,14 @@
package com.android.server.backup.utils;
-import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
import static com.android.server.backup.BackupManagerService.TAG;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.Signature;
-import android.content.pm.SigningInfo;
-import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.util.Slog;
-import android.util.StringBuilderPrinter;
import java.io.DataInputStream;
import java.io.EOFException;
-import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -68,67 +59,4 @@
}
}
}
-
- /**
- * Writes app manifest to the given manifest file.
- *
- * @param pkg - app package, which manifest to write.
- * @param packageManager - {@link PackageManager} instance.
- * @param manifestFile - target manifest file.
- * @param withApk - whether include apk or not.
- * @param withWidgets - whether to write widgets data.
- * @throws IOException - in case of an error.
- */
- // TODO: withWidgets is not used, decide whether it is needed.
- public static void writeAppManifest(PackageInfo pkg, PackageManager packageManager,
- File manifestFile, boolean withApk, boolean withWidgets) throws IOException {
- // Manifest format. All data are strings ending in LF:
- // BACKUP_MANIFEST_VERSION, currently 1
- //
- // Version 1:
- // package name
- // package's versionCode
- // platform versionCode
- // getInstallerPackageName() for this package (maybe empty)
- // boolean: "1" if archive includes .apk; any other string means not
- // number of signatures == N
- // N*: signature byte array in ascii format per Signature.toCharsString()
- StringBuilder builder = new StringBuilder(4096);
- StringBuilderPrinter printer = new StringBuilderPrinter(builder);
-
- printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
- printer.println(pkg.packageName);
- printer.println(Long.toString(pkg.getLongVersionCode()));
- printer.println(Integer.toString(Build.VERSION.SDK_INT));
-
- String installerName = packageManager.getInstallerPackageName(pkg.packageName);
- printer.println((installerName != null) ? installerName : "");
-
- printer.println(withApk ? "1" : "0");
-
- // write the signature block
- SigningInfo signingInfo = pkg.signingInfo;
- if (signingInfo == null) {
- printer.println("0");
- } else {
- // retrieve the newest sigs to write
- // TODO (b/73988180) use entire signing history in case of rollbacks
- Signature[] signatures = signingInfo.getApkContentsSigners();
- printer.println(Integer.toString(signatures.length));
- for (Signature sig : signatures) {
- printer.println(sig.toCharsString());
- }
- }
-
- FileOutputStream outstream = new FileOutputStream(manifestFile);
- outstream.write(builder.toString().getBytes());
- outstream.close();
-
- // We want the manifest block in the archive stream to be idempotent:
- // each time we generate a backup stream for the app, we want the manifest
- // block to be identical. The underlying tar mechanism sees it as a file,
- // though, and will propagate its mtime, causing the tar header to vary.
- // Avoid this problem by pinning the mtime to zero.
- manifestFile.setLastModified(0);
- }
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 26ef42f..499c03d 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -52,6 +52,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.Message;
import android.os.ParcelableException;
import android.os.PowerManager;
@@ -111,7 +112,7 @@
import java.util.function.Predicate;
/**
- * Alarm manager implementaion.
+ * Alarm manager implementation.
*
* Unit test:
atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -138,7 +139,6 @@
static final boolean DEBUG_STANDBY = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
- static final int ALARM_EVENT = 1;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
// Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays
@@ -169,7 +169,6 @@
// List of alarms per uid deferred due to user applied background restrictions on the source app
SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>();
- long mNativeData;
private long mNextWakeup;
private long mNextNonWakeup;
private long mNextWakeUpSetAt;
@@ -181,15 +180,14 @@
private long mLastTickReceived;
private long mLastTickAdded;
private long mLastTickRemoved;
+ private final Injector mInjector;
int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock;
boolean mLastWakeLockUnimportantForLogging;
ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
ArrayList<InFlight> mInFlight = new ArrayList<>();
- final AlarmHandler mHandler = new AlarmHandler();
+ AlarmHandler mHandler;
ClockReceiver mClockReceiver;
- InteractiveStateReceiver mInteractiveStateReceiver;
- private UninstallReceiver mUninstallReceiver;
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
PendingIntent mTimeTickSender;
PendingIntent mDateChangeSender;
@@ -277,7 +275,8 @@
* global Settings. Any access to this class or its fields should be done while
* holding the AlarmManagerService.mLock lock.
*/
- private final class Constants extends ContentObserver {
+ @VisibleForTesting
+ final class Constants extends ContentObserver {
// Key names stored in the settings value.
private static final String KEY_MIN_FUTURITY = "min_futurity";
private static final String KEY_MIN_INTERVAL = "min_interval";
@@ -456,7 +455,7 @@
}
}
- final Constants mConstants;
+ Constants mConstants;
// Alarm delivery ordering bookkeeping
static final int PRIO_TICK = 0;
@@ -510,7 +509,7 @@
flags = seed.flags;
alarms.add(seed);
if (seed.operation == mTimeTickSender) {
- mLastTickAdded = System.currentTimeMillis();
+ mLastTickAdded = mInjector.getCurrentTimeMillis();
}
}
@@ -535,7 +534,7 @@
}
alarms.add(index, alarm);
if (alarm.operation == mTimeTickSender) {
- mLastTickAdded = System.currentTimeMillis();
+ mLastTickAdded = mInjector.getCurrentTimeMillis();
}
if (DEBUG_BATCH) {
Slog.v(TAG, "Adding " + alarm + " to " + this);
@@ -573,7 +572,7 @@
mNextAlarmClockMayChange = true;
}
if (alarm.operation == mTimeTickSender) {
- mLastTickRemoved = System.currentTimeMillis();
+ mLastTickRemoved = mInjector.getCurrentTimeMillis();
}
} else {
if (alarm.whenElapsed > newStart) {
@@ -734,17 +733,20 @@
Alarm mNextWakeFromIdle = null;
ArrayList<Alarm> mPendingWhileIdleAlarms = new ArrayList<>();
- public AlarmManagerService(Context context) {
+ @VisibleForTesting
+ AlarmManagerService(Context context, Injector injector) {
super(context);
- mConstants = new Constants(mHandler);
-
- publishLocalService(AlarmManagerInternal.class, new LocalService());
+ mInjector = injector;
}
- static long convertToElapsed(long when, int type) {
+ AlarmManagerService(Context context) {
+ this(context, new Injector(context));
+ }
+
+ private long convertToElapsed(long when, int type) {
final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
if (isRtc) {
- when -= System.currentTimeMillis() - SystemClock.elapsedRealtime();
+ when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtime();
}
return when;
}
@@ -854,7 +856,7 @@
ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone();
mAlarmBatches.clear();
Alarm oldPendingIdleUntil = mPendingIdleUntil;
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowElapsed = mInjector.getElapsedRealtime();
final int oldBatches = oldSet.size();
for (int batchNum = 0; batchNum < oldBatches; batchNum++) {
Batch batch = oldSet.get(batchNum);
@@ -984,7 +986,7 @@
alarmsToDeliver = alarmsForUid;
mPendingBackgroundAlarms.remove(uid);
}
- deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
+ deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime());
}
/**
@@ -1001,7 +1003,7 @@
mPendingBackgroundAlarms, alarmsToDeliver, this::isBackgroundRestricted);
if (alarmsToDeliver.size() > 0) {
- deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, SystemClock.elapsedRealtime());
+ deliverPendingBackgroundAlarmsLocked(alarmsToDeliver, mInjector.getElapsedRealtime());
}
}
@@ -1088,7 +1090,7 @@
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = 0;
ent.pkg = "FINISH IDLE";
- ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ ent.elapsedRealtime = mInjector.getElapsedRealtime();
mAllowWhileIdleDispatches.add(ent);
}
@@ -1096,7 +1098,7 @@
if (mPendingWhileIdleAlarms.size() > 0) {
ArrayList<Alarm> alarms = mPendingWhileIdleAlarms;
mPendingWhileIdleAlarms = new ArrayList<>();
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowElapsed = mInjector.getElapsedRealtime();
for (int i=alarms.size() - 1; i >= 0; i--) {
Alarm a = alarms.get(i);
reAddAlarmLocked(a, nowElapsed, false);
@@ -1282,121 +1284,114 @@
@Override
public void onStart() {
- mNativeData = init();
- mNextWakeup = mNextNonWakeup = 0;
+ mInjector.init();
- // We have to set current TimeZone info to kernel
- // because kernel doesn't keep this after reboot
- setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
+ synchronized (mLock) {
+ mHandler = new AlarmHandler(Looper.myLooper());
+ mConstants = new Constants(mHandler);
- // Also sure that we're booting with a halfway sensible current time
- if (mNativeData != 0) {
+ mNextWakeup = mNextNonWakeup = 0;
+
+ // We have to set current TimeZone info to kernel
+ // because kernel doesn't keep this after reboot
+ setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY));
+
+ // Also sure that we're booting with a halfway sensible current time
final long systemBuildTime = Environment.getRootDirectory().lastModified();
- if (System.currentTimeMillis() < systemBuildTime) {
- Slog.i(TAG, "Current time only " + System.currentTimeMillis()
+ if (mInjector.getCurrentTimeMillis() < systemBuildTime) {
+ Slog.i(TAG, "Current time only " + mInjector.getCurrentTimeMillis()
+ ", advancing to build time " + systemBuildTime);
- setKernelTime(mNativeData, systemBuildTime);
+ mInjector.setKernelTime(systemBuildTime);
}
- }
- // Determine SysUI's uid
- final PackageManager packMan = getContext().getPackageManager();
- try {
- PermissionInfo sysUiPerm = packMan.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0);
- ApplicationInfo sysUi = packMan.getApplicationInfo(sysUiPerm.packageName, 0);
- if ((sysUi.privateFlags&ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
- mSystemUiUid = sysUi.uid;
+ // Determine SysUI's uid
+ mSystemUiUid = mInjector.getSystemUiUid();
+ if (mSystemUiUid <= 0) {
+ Slog.wtf(TAG, "SysUI package not found!");
+ }
+ mWakeLock = mInjector.getAlarmWakeLock();
+
+ mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
+ new Intent(Intent.ACTION_TIME_TICK).addFlags(
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0,
+ UserHandle.ALL);
+ Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
+
+ mClockReceiver = mInjector.getClockReceiver(this);
+ new InteractiveStateReceiver();
+ new UninstallReceiver();
+
+ if (mInjector.isAlarmDriverPresent()) {
+ AlarmThread waitThread = new AlarmThread();
+ waitThread.start();
} else {
- Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION
- + " defined by non-privileged app " + sysUi.packageName
- + " - ignoring");
+ Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
}
- } catch (NameNotFoundException e) {
+
+ try {
+ ActivityManager.getService().registerUidObserver(new UidObserver(),
+ ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
+ | ActivityManager.UID_OBSERVER_ACTIVE,
+ ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
}
-
- if (mSystemUiUid <= 0) {
- Slog.wtf(TAG, "SysUI package not found!");
- }
-
- PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");
-
- mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
- new Intent(Intent.ACTION_TIME_TICK).addFlags(
- Intent.FLAG_RECEIVER_REGISTERED_ONLY
- | Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0,
- UserHandle.ALL);
- Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
- | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
- mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
- Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
-
- // now that we have initied the driver schedule the alarm
- mClockReceiver = new ClockReceiver();
- mClockReceiver.scheduleTimeTickEvent();
- mClockReceiver.scheduleDateChangedEvent();
- mInteractiveStateReceiver = new InteractiveStateReceiver();
- mUninstallReceiver = new UninstallReceiver();
-
- if (mNativeData != 0) {
- AlarmThread waitThread = new AlarmThread();
- waitThread.start();
- } else {
- Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler.");
- }
-
- try {
- ActivityManager.getService().registerUidObserver(new UidObserver(),
- ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE
- | ActivityManager.UID_OBSERVER_ACTIVE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null);
- } catch (RemoteException e) {
- // ignored; both services live in system_server
- }
+ publishLocalService(AlarmManagerInternal.class, new LocalService());
publishBinderService(Context.ALARM_SERVICE, mService);
}
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
- mConstants.start(getContext().getContentResolver());
- mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
- mLocalDeviceIdleController
- = LocalServices.getService(DeviceIdleController.LocalService.class);
- mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
- mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());
+ synchronized (mLock) {
+ mConstants.start(getContext().getContentResolver());
+ mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
+ mLocalDeviceIdleController =
+ LocalServices.getService(DeviceIdleController.LocalService.class);
+ mUsageStatsManagerInternal =
+ LocalServices.getService(UsageStatsManagerInternal.class);
+ mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());
- mAppStateTracker = LocalServices.getService(AppStateTracker.class);
- mAppStateTracker.addListener(mForceAppStandbyListener);
+ mAppStateTracker = LocalServices.getService(AppStateTracker.class);
+ mAppStateTracker.addListener(mForceAppStandbyListener);
+
+ mClockReceiver.scheduleTimeTickEvent();
+ mClockReceiver.scheduleDateChangedEvent();
+ }
}
}
@Override
protected void finalize() throws Throwable {
try {
- close(mNativeData);
+ mInjector.close();
} finally {
super.finalize();
}
}
boolean setTimeImpl(long millis) {
- if (mNativeData == 0) {
+ if (!mInjector.isAlarmDriverPresent()) {
Slog.w(TAG, "Not setting time since no alarm driver is available.");
return false;
}
synchronized (mLock) {
- final long currentTimeMillis = System.currentTimeMillis();
- setKernelTime(mNativeData, millis);
+ final long currentTimeMillis = mInjector.getCurrentTimeMillis();
+ mInjector.setKernelTime(millis);
final TimeZone timeZone = TimeZone.getDefault();
final int currentTzOffset = timeZone.getOffset(currentTimeMillis);
final int newTzOffset = timeZone.getOffset(millis);
if (currentTzOffset != newTzOffset) {
Slog.i(TAG, "Timezone offset has changed, updating kernel timezone");
- setKernelTimezone(mNativeData, -(newTzOffset / 60000));
+ mInjector.setKernelTimezone(-(newTzOffset / 60000));
}
// The native implementation of setKernelTime can return -1 even when the kernel
// time was set correctly, so assume setting kernel time was successful and always
@@ -1426,8 +1421,8 @@
// Update the kernel timezone information
// Kernel tracks time offsets as 'minutes west of GMT'
- int gmtOffset = zone.getOffset(System.currentTimeMillis());
- setKernelTimezone(mNativeData, -(gmtOffset / 60000));
+ int gmtOffset = zone.getOffset(mInjector.getCurrentTimeMillis());
+ mInjector.setKernelTimezone(-(gmtOffset / 60000));
}
TimeZone.setDefault(null);
@@ -1498,7 +1493,7 @@
triggerAtTime = 0;
}
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowElapsed = mInjector.getElapsedRealtime();
final long nominalTrigger = convertToElapsed(triggerAtTime, type);
// Try to prevent spamming by making sure we aren't firing alarms in the immediate future
final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY;
@@ -1551,7 +1546,8 @@
* Return the minimum time that should elapse before an app in the specified bucket
* can receive alarms again
*/
- private long getMinDelayForBucketLocked(int bucket) {
+ @VisibleForTesting
+ long getMinDelayForBucketLocked(int bucket) {
// UsageStats bucket values are treated as floors of their behavioral range.
// In other words, a bucket value between WORKING and ACTIVE is treated as
// WORKING, not as ACTIVE. The ACTIVE and NEVER bucket apply only at specific
@@ -1591,7 +1587,7 @@
final String sourcePackage = alarm.sourcePackage;
final int sourceUserId = UserHandle.getUserId(alarm.creatorUid);
final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket(
- sourcePackage, sourceUserId, SystemClock.elapsedRealtime());
+ sourcePackage, sourceUserId, mInjector.getElapsedRealtime());
final Pair<String, Integer> packageUser = Pair.create(sourcePackage, sourceUserId);
final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
@@ -1619,7 +1615,7 @@
a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed;
}
// Add fuzz to make the alarm go off some time before the actual desired time.
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowElapsed = mInjector.getElapsedRealtime();
final int fuzz = fuzzForDuration(a.whenElapsed-nowElapsed);
if (fuzz > 0) {
if (mRandom == null) {
@@ -1655,7 +1651,7 @@
ent.pkg = a.operation.getCreatorPackage();
ent.tag = a.operation.getTag("");
ent.op = "SET";
- ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ ent.elapsedRealtime = mInjector.getElapsedRealtime();
ent.argRealtime = a.whenElapsed;
mAllowWhileIdleDispatches.add(ent);
}
@@ -1675,7 +1671,7 @@
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = 0;
ent.pkg = "START IDLE";
- ent.elapsedRealtime = SystemClock.elapsedRealtime();
+ ent.elapsedRealtime = mInjector.getElapsedRealtime();
mAllowWhileIdleDispatches.add(ent);
}
}
@@ -1890,8 +1886,8 @@
pw.println(" App Standby Parole: " + mAppStandbyParole);
pw.println();
- final long nowRTC = System.currentTimeMillis();
- final long nowELAPSED = SystemClock.elapsedRealtime();
+ final long nowRTC = mInjector.getCurrentTimeMillis();
+ final long nowELAPSED = mInjector.getElapsedRealtime();
final long nowUPTIME = SystemClock.uptimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
@@ -1958,10 +1954,10 @@
pw.println();
pw.print(" Next kernel non-wakeup alarm: ");
- TimeUtils.formatDuration(getNextAlarm(mNativeData, ELAPSED_REALTIME), pw);
+ TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME), pw);
pw.println();
pw.print(" Next kernel wakeup alarm: ");
- TimeUtils.formatDuration(getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP), pw);
+ TimeUtils.formatDuration(mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP), pw);
pw.println();
pw.print(" Last wakeup: "); TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw);
@@ -2257,8 +2253,8 @@
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mLock) {
- final long nowRTC = System.currentTimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowRTC = mInjector.getCurrentTimeMillis();
+ final long nowElapsed = mInjector.getElapsedRealtime();
proto.write(AlarmManagerServiceDumpProto.CURRENT_TIME, nowRTC);
proto.write(AlarmManagerServiceDumpProto.ELAPSED_REALTIME, nowElapsed);
proto.write(AlarmManagerServiceDumpProto.LAST_TIME_CHANGE_CLOCK_TIME,
@@ -2494,8 +2490,8 @@
private void logBatchesLocked(SimpleDateFormat sdf) {
ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
PrintWriter pw = new PrintWriter(bs);
- final long nowRTC = System.currentTimeMillis();
- final long nowELAPSED = SystemClock.elapsedRealtime();
+ final long nowRTC = mInjector.getCurrentTimeMillis();
+ final long nowELAPSED = mInjector.getElapsedRealtime();
final int NZ = mAlarmBatches.size();
for (int iz = 0; iz < NZ; iz++) {
Batch bz = mAlarmBatches.get(iz);
@@ -2693,7 +2689,7 @@
TimeUtils.formatDuration(mNextNonWakeUpSetAt - nowElapsed, errorMsg);
errorMsg.append(", mLastWakeup=");
TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
- errorMsg.append(", timerfd_gettime=" + getNextAlarm(mNativeData, ELAPSED_REALTIME));
+ errorMsg.append(", timerfd_gettime=" + mInjector.getNextAlarm(ELAPSED_REALTIME));
errorMsg.append("];");
}
if (mNextWakeup < (nowElapsed - 10_000) && mLastWakeup < mNextWakeup) {
@@ -2705,7 +2701,7 @@
errorMsg.append(", mLastWakeup=");
TimeUtils.formatDuration(mLastWakeup - nowElapsed, errorMsg);
errorMsg.append(", timerfd_gettime="
- + getNextAlarm(mNativeData, ELAPSED_REALTIME_WAKEUP));
+ + mInjector.getNextAlarm(ELAPSED_REALTIME_WAKEUP));
errorMsg.append("];");
}
if (stuck) {
@@ -2716,7 +2712,7 @@
void rescheduleKernelAlarmsLocked() {
// Schedule the next upcoming wakeup alarm. If there is a deliverable batch
// prior to that which contains no wakeups, we schedule that as well.
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowElapsed = mInjector.getElapsedRealtime();
validateLastAlarmExpiredLocked(nowElapsed);
long nextNonWakeup = 0;
if (mAlarmBatches.size() > 0) {
@@ -2743,7 +2739,7 @@
}
}
- private void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
+ void removeLocked(PendingIntent operation, IAlarmListener directReceiver) {
if (operation == null && directReceiver == null) {
if (localLOGV) {
Slog.w(TAG, "requested remove() of null operation",
@@ -2983,7 +2979,7 @@
void interactiveStateChangedLocked(boolean interactive) {
if (mInteractive != interactive) {
mInteractive = interactive;
- final long nowELAPSED = SystemClock.elapsedRealtime();
+ final long nowELAPSED = mInjector.getElapsedRealtime();
if (interactive) {
if (mPendingNonWakeupAlarms.size() > 0) {
final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
@@ -3023,31 +3019,13 @@
}
private void setLocked(int type, long when) {
- if (mNativeData != 0) {
- // The kernel never triggers alarms with negative wakeup times
- // so we ensure they are positive.
- long alarmSeconds, alarmNanoseconds;
- if (when < 0) {
- alarmSeconds = 0;
- alarmNanoseconds = 0;
- } else {
- alarmSeconds = when / 1000;
- alarmNanoseconds = (when % 1000) * 1000 * 1000;
- }
-
- final int result = set(mNativeData, type, alarmSeconds, alarmNanoseconds);
- if (result != 0) {
- final long nowElapsed = SystemClock.elapsedRealtime();
- Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed
- + " type=" + type + " when=" + when
- + " @ (" + alarmSeconds + "," + alarmNanoseconds
- + "), ret = " + result + " = " + Os.strerror(result));
- }
+ if (mInjector.isAlarmDriverPresent()) {
+ mInjector.setAlarm(type, when);
} else {
Message msg = Message.obtain();
- msg.what = ALARM_EVENT;
+ msg.what = AlarmHandler.ALARM_EVENT;
- mHandler.removeMessages(ALARM_EVENT);
+ mHandler.removeMessages(msg.what);
mHandler.sendMessageAtTime(msg, when);
}
}
@@ -3106,13 +3084,13 @@
exemptOnBatterySaver);
}
- private native long init();
- private native void close(long nativeData);
- private native int set(long nativeData, int type, long seconds, long nanoseconds);
- private native int waitForAlarm(long nativeData);
- private native int setKernelTime(long nativeData, long millis);
- private native int setKernelTimezone(long nativeData, int minuteswest);
- private native long getNextAlarm(long nativeData, int type);
+ private static native long init();
+ private static native void close(long nativeData);
+ private static native int set(long nativeData, int type, long seconds, long nanoseconds);
+ private static native int waitForAlarm(long nativeData);
+ private static native int setKernelTime(long nativeData, long millis);
+ private static native int setKernelTimezone(long nativeData, int minuteswest);
+ private static native long getNextAlarm(long nativeData, int type);
private long getWhileIdleMinIntervalLocked(int uid) {
final boolean dozing = mPendingIdleUntil != null;
@@ -3516,6 +3494,103 @@
|| (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0;
}
+ @VisibleForTesting
+ static class Injector {
+ private long mNativeData;
+ private Context mContext;
+
+ Injector(Context context) {
+ mContext = context;
+ }
+
+ void init() {
+ mNativeData = AlarmManagerService.init();
+ }
+
+ int waitForAlarm() {
+ return AlarmManagerService.waitForAlarm(mNativeData);
+ }
+
+ boolean isAlarmDriverPresent() {
+ return mNativeData != 0;
+ }
+
+ void setAlarm(int type, long millis) {
+ // The kernel never triggers alarms with negative wakeup times
+ // so we ensure they are positive.
+ final long alarmSeconds, alarmNanoseconds;
+ if (millis < 0) {
+ alarmSeconds = 0;
+ alarmNanoseconds = 0;
+ } else {
+ alarmSeconds = millis / 1000;
+ alarmNanoseconds = (millis % 1000) * 1000 * 1000;
+ }
+
+ final int result = AlarmManagerService.set(mNativeData, type, alarmSeconds,
+ alarmNanoseconds);
+ if (result != 0) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed
+ + " type=" + type + " @ (" + alarmSeconds + "," + alarmNanoseconds
+ + "), ret = " + result + " = " + Os.strerror(result));
+ }
+ }
+
+ long getNextAlarm(int type) {
+ return AlarmManagerService.getNextAlarm(mNativeData, type);
+ }
+
+ void setKernelTimezone(int minutesWest) {
+ AlarmManagerService.setKernelTimezone(mNativeData, minutesWest);
+ }
+
+ void setKernelTime(long millis) {
+ if (mNativeData != 0) {
+ AlarmManagerService.setKernelTime(mNativeData, millis);
+ }
+ }
+
+ void close() {
+ AlarmManagerService.close(mNativeData);
+ }
+
+ long getElapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ long getCurrentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ PowerManager.WakeLock getAlarmWakeLock() {
+ final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ return pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*alarm*");
+ }
+
+ int getSystemUiUid() {
+ int sysUiUid = -1;
+ final PackageManager pm = mContext.getPackageManager();
+ try {
+ PermissionInfo sysUiPerm = pm.getPermissionInfo(SYSTEM_UI_SELF_PERMISSION, 0);
+ ApplicationInfo sysUi = pm.getApplicationInfo(sysUiPerm.packageName, 0);
+ if ((sysUi.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ sysUiUid = sysUi.uid;
+ } else {
+ Slog.e(TAG, "SysUI permission " + SYSTEM_UI_SELF_PERMISSION
+ + " defined by non-privileged app " + sysUi.packageName
+ + " - ignoring");
+ }
+ } catch (NameNotFoundException e) {
+ }
+ return sysUiUid;
+ }
+
+ ClockReceiver getClockReceiver(AlarmManagerService service) {
+ return service.new ClockReceiver();
+ }
+ }
+
private class AlarmThread extends Thread
{
private int mFalseWakeups;
@@ -3524,7 +3599,7 @@
{
super("AlarmManager");
mFalseWakeups = 0;
- mWtfThreshold = 10;
+ mWtfThreshold = 100;
}
public void run()
@@ -3533,14 +3608,16 @@
while (true)
{
- int result = waitForAlarm(mNativeData);
-
- final long nowRTC = System.currentTimeMillis();
- final long nowELAPSED = SystemClock.elapsedRealtime();
+ int result = mInjector.waitForAlarm();
+ final long nowRTC = mInjector.getCurrentTimeMillis();
+ final long nowELAPSED = mInjector.getElapsedRealtime();
synchronized (mLock) {
mLastWakeup = nowELAPSED;
}
-
+ if (result == 0) {
+ Slog.wtf(TAG, "waitForAlarm returned 0, nowRTC = " + nowRTC
+ + ", nowElapsed = " + nowELAPSED);
+ }
triggerList.clear();
if ((result & TIME_CHANGED_MASK) != 0) {
@@ -3720,7 +3797,8 @@
public static final int APP_STANDBY_PAROLE_CHANGED = 6;
public static final int REMOVE_FOR_STOPPED = 7;
- public AlarmHandler() {
+ AlarmHandler(Looper looper) {
+ super(looper);
}
public void postRemoveForStopped(int uid) {
@@ -3732,8 +3810,8 @@
case ALARM_EVENT: {
ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
synchronized (mLock) {
- final long nowRTC = System.currentTimeMillis();
- final long nowELAPSED = SystemClock.elapsedRealtime();
+ final long nowRTC = mInjector.getCurrentTimeMillis();
+ final long nowELAPSED = mInjector.getElapsedRealtime();
triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
updateNextAlarmClockLocked();
}
@@ -3817,7 +3895,7 @@
Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
}
synchronized (mLock) {
- mLastTickReceived = System.currentTimeMillis();
+ mLastTickReceived = mInjector.getCurrentTimeMillis();
}
scheduleTimeTickEvent();
} else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
@@ -3826,14 +3904,14 @@
// based off of the current Zone gmt offset + userspace tracked
// daylight savings information.
TimeZone zone = TimeZone.getTimeZone(SystemProperties.get(TIMEZONE_PROPERTY));
- int gmtOffset = zone.getOffset(System.currentTimeMillis());
- setKernelTimezone(mNativeData, -(gmtOffset / 60000));
+ int gmtOffset = zone.getOffset(mInjector.getCurrentTimeMillis());
+ mInjector.setKernelTimezone(-(gmtOffset / 60000));
scheduleDateChangedEvent();
}
}
public void scheduleTimeTickEvent() {
- final long currentTime = System.currentTimeMillis();
+ final long currentTime = mInjector.getCurrentTimeMillis();
final long nextTime = 60000 * ((currentTime / 60000) + 1);
// Schedule this event for the amount of time that it would take to get to
@@ -3841,7 +3919,7 @@
final long tickEventDelay = nextTime - currentTime;
final WorkSource workSource = null; // Let system take blame for time tick events.
- setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
+ setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource,
null, Process.myUid(), "android");
@@ -3853,7 +3931,7 @@
public void scheduleDateChangedEvent() {
Calendar calendar = Calendar.getInstance();
- calendar.setTimeInMillis(System.currentTimeMillis());
+ calendar.setTimeInMillis(mInjector.getCurrentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
@@ -4122,7 +4200,7 @@
}
private void updateStatsLocked(InFlight inflight) {
- final long nowELAPSED = SystemClock.elapsedRealtime();
+ final long nowELAPSED = mInjector.getElapsedRealtime();
BroadcastStats bs = inflight.mBroadcastStats;
bs.nesting--;
if (bs.nesting <= 0) {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 50f15ca0..a85b69b 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -399,6 +399,12 @@
private void update(android.hardware.health.V2_0.HealthInfo info) {
traceBegin("HealthInfoUpdate");
+
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
+ info.legacy.batteryChargeCounter);
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
+ info.legacy.batteryCurrent);
+
synchronized (mLock) {
if (!mUpdatesStopped) {
mHealthInfo = info.legacy;
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 9a7c345..15673a7 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -33,7 +33,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BinderCallsStats;
-import com.android.internal.os.BinderInternal;
+import com.android.internal.os.CachedDeviceState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -41,7 +41,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Random;
public class BinderCallsStatsService extends Binder {
@@ -156,8 +155,10 @@
@Override
public void onBootPhase(int phase) {
if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+ CachedDeviceState.Readonly deviceState = getLocalService(
+ CachedDeviceState.Readonly.class);
mService.systemReady(getContext());
- mBinderCallsStats.systemReady(getContext());
+ mBinderCallsStats.setDeviceState(deviceState);
}
}
}
diff --git a/services/core/java/com/android/server/CachedDeviceStateService.java b/services/core/java/com/android/server/CachedDeviceStateService.java
new file mode 100644
index 0000000..38269d3
--- /dev/null
+++ b/services/core/java/com/android/server/CachedDeviceStateService.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
+import android.util.Slog;
+
+import com.android.internal.os.CachedDeviceState;
+
+/**
+ * Tracks changes to the device state (e.g. charging/on battery, screen on/off) to share it with
+ * the System Server telemetry services.
+ *
+ * @hide Only for use within the system server.
+ */
+public class CachedDeviceStateService extends SystemService {
+ private static final String TAG = "CachedDeviceStateService";
+ private final CachedDeviceState mDeviceState = new CachedDeviceState();
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_BATTERY_CHANGED:
+ mDeviceState.setCharging(
+ intent.getIntExtra(BatteryManager.EXTRA_PLUGGED,
+ OsProtoEnums.BATTERY_PLUGGED_NONE)
+ != OsProtoEnums.BATTERY_PLUGGED_NONE);
+ break;
+ case Intent.ACTION_SCREEN_ON:
+ mDeviceState.setScreenInteractive(true);
+ break;
+ case Intent.ACTION_SCREEN_OFF:
+ mDeviceState.setScreenInteractive(false);
+ break;
+ }
+ }
+ };
+
+ public CachedDeviceStateService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishLocalService(CachedDeviceState.Readonly.class, mDeviceState.getReadonlyClient());
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ getContext().registerReceiver(mBroadcastReceiver, filter);
+ mDeviceState.setCharging(queryIsCharging());
+ mDeviceState.setScreenInteractive(queryScreenInteractive(getContext()));
+ }
+ }
+
+ private boolean queryIsCharging() {
+ final BatteryManagerInternal batteryManager =
+ LocalServices.getService(BatteryManagerInternal.class);
+ if (batteryManager == null) {
+ Slog.wtf(TAG, "BatteryManager null while starting CachedDeviceStateService");
+ // Default to true to not collect any data.
+ return true;
+ } else {
+ return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
+ }
+ }
+
+ private boolean queryScreenInteractive(Context context) {
+ final PowerManager powerManager = context.getSystemService(PowerManager.class);
+ if (powerManager == null) {
+ Slog.wtf(TAG, "PowerManager null while starting CachedDeviceStateService");
+ return false;
+ } else {
+ return powerManager.isInteractive();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ba5f323..7602090 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -183,6 +183,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -202,6 +203,8 @@
private static final String DIAG_ARG = "--diag";
public static final String SHORT_ARG = "--short";
private static final String TETHERING_ARG = "tethering";
+ private static final String NETWORK_ARG = "networks";
+ private static final String REQUEST_ARG = "requests";
private static final boolean DBG = true;
private static final boolean VDBG = false;
@@ -1978,7 +1981,7 @@
private void dumpNetworkDiagnostics(IndentingPrintWriter pw) {
final List<NetworkDiagnostics> netDiags = new ArrayList<NetworkDiagnostics>();
final long DIAG_TIME_MS = 5000;
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ for (NetworkAgentInfo nai : networksSortedById()) {
// Start gathering diagnostic information.
netDiags.add(new NetworkDiagnostics(
nai.network,
@@ -2009,6 +2012,12 @@
} else if (ArrayUtils.contains(args, TETHERING_ARG)) {
mTethering.dump(fd, pw, args);
return;
+ } else if (ArrayUtils.contains(args, NETWORK_ARG)) {
+ dumpNetworks(pw);
+ return;
+ } else if (ArrayUtils.contains(args, REQUEST_ARG)) {
+ dumpNetworkRequests(pw);
+ return;
}
pw.print("NetworkFactories for:");
@@ -2029,36 +2038,15 @@
pw.println("Current Networks:");
pw.increaseIndent();
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- pw.println(nai.toString());
- pw.increaseIndent();
- pw.println(String.format(
- "Requests: REQUEST:%d LISTEN:%d BACKGROUND_REQUEST:%d total:%d",
- nai.numForegroundNetworkRequests(),
- nai.numNetworkRequests() - nai.numRequestNetworkRequests(),
- nai.numBackgroundNetworkRequests(),
- nai.numNetworkRequests()));
- pw.increaseIndent();
- for (int i = 0; i < nai.numNetworkRequests(); i++) {
- pw.println(nai.requestAt(i).toString());
- }
- pw.decreaseIndent();
- pw.println("Lingered:");
- pw.increaseIndent();
- nai.dumpLingerTimers(pw);
- pw.decreaseIndent();
- pw.decreaseIndent();
- }
+ dumpNetworks(pw);
pw.decreaseIndent();
pw.println();
pw.println("Network Requests:");
pw.increaseIndent();
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- pw.println(nri.toString());
- }
- pw.println();
+ dumpNetworkRequests(pw);
pw.decreaseIndent();
+ pw.println();
mLegacyTypeTracker.dump(pw);
@@ -2126,6 +2114,55 @@
}
}
+ private void dumpNetworks(IndentingPrintWriter pw) {
+ for (NetworkAgentInfo nai : networksSortedById()) {
+ pw.println(nai.toString());
+ pw.increaseIndent();
+ pw.println(String.format(
+ "Requests: REQUEST:%d LISTEN:%d BACKGROUND_REQUEST:%d total:%d",
+ nai.numForegroundNetworkRequests(),
+ nai.numNetworkRequests() - nai.numRequestNetworkRequests(),
+ nai.numBackgroundNetworkRequests(),
+ nai.numNetworkRequests()));
+ pw.increaseIndent();
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ pw.println(nai.requestAt(i).toString());
+ }
+ pw.decreaseIndent();
+ pw.println("Lingered:");
+ pw.increaseIndent();
+ nai.dumpLingerTimers(pw);
+ pw.decreaseIndent();
+ pw.decreaseIndent();
+ }
+ }
+
+ private void dumpNetworkRequests(IndentingPrintWriter pw) {
+ for (NetworkRequestInfo nri : requestsSortedById()) {
+ pw.println(nri.toString());
+ }
+ }
+
+ /**
+ * Return an array of all current NetworkAgentInfos sorted by network id.
+ */
+ private NetworkAgentInfo[] networksSortedById() {
+ NetworkAgentInfo[] networks = new NetworkAgentInfo[0];
+ networks = mNetworkAgentInfos.values().toArray(networks);
+ Arrays.sort(networks, Comparator.comparingInt(nai -> nai.network.netId));
+ return networks;
+ }
+
+ /**
+ * Return an array of all current NetworkRequest sorted by request id.
+ */
+ private NetworkRequestInfo[] requestsSortedById() {
+ NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
+ requests = mNetworkRequests.values().toArray(requests);
+ Arrays.sort(requests, Comparator.comparingInt(nri -> nri.request.requestId));
+ return requests;
+ }
+
private boolean isLiveNetworkAgent(NetworkAgentInfo nai, int what) {
if (nai.network == null) return false;
final NetworkAgentInfo officialNai = getNetworkAgentInfoForNetwork(nai.network);
@@ -2916,7 +2953,7 @@
pw.println("User setting: " + description);
pw.println("Network overrides:");
pw.increaseIndent();
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ for (NetworkAgentInfo nai : networksSortedById()) {
if (nai.avoidUnvalidated) {
pw.println(nai.name());
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 665c6b7..93d023d 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -27,6 +27,7 @@
import android.annotation.AnyThread;
import android.annotation.BinderThread;
import android.annotation.ColorInt;
+import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -127,6 +128,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.inputmethod.IInputContentUriToken;
+import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import com.android.internal.inputmethod.InputMethodUtils;
@@ -198,14 +200,12 @@
static final int MSG_SHOW_SOFT_INPUT = 1020;
static final int MSG_HIDE_SOFT_INPUT = 1030;
static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
- static final int MSG_ATTACH_TOKEN = 1040;
+ static final int MSG_INITIALIZE_IME = 1040;
static final int MSG_CREATE_SESSION = 1050;
static final int MSG_START_INPUT = 2000;
static final int MSG_START_VR_INPUT = 2010;
- static final int MSG_ADD_CLIENT = 2980;
- static final int MSG_REMOVE_CLIENT = 2990;
static final int MSG_UNBIND_CLIENT = 3000;
static final int MSG_BIND_CLIENT = 3010;
static final int MSG_SET_ACTIVE = 3020;
@@ -1302,7 +1302,7 @@
@Override
public void onStart() {
LocalServices.addService(InputMethodManagerInternal.class,
- new LocalServiceImpl(mService.mHandler));
+ new LocalServiceImpl(mService));
publishBinderService(Context.INPUT_METHOD_SERVICE, mService);
}
@@ -1561,7 +1561,6 @@
final String defaultImiId = mSettings.getSelectedInputMethod();
final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
buildInputMethodListLocked(!imeSelectedOnBoot /* resetDefaultEnabledIme */);
- resetDefaultImeLocked(mContext);
updateFromSettingsLocked(true);
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
mSettings.getEnabledInputMethodListLocked(), currentUserId,
@@ -1971,7 +1970,7 @@
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
+ MSG_INITIALIZE_IME, mCurMethod, mCurToken));
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
requestClientSessionLocked(mCurClient);
@@ -2115,8 +2114,9 @@
}
}
- @Override
- public void updateStatusIcon(IBinder token, String packageName, int iconId) {
+ @BinderThread
+ private void updateStatusIcon(@NonNull IBinder token, String packageName,
+ @DrawableRes int iconId) {
synchronized (mMethodMap) {
if (!calledWithValidToken(token)) {
return;
@@ -2220,8 +2220,7 @@
@BinderThread
@SuppressWarnings("deprecation")
- @Override
- public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
+ private void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
if (!calledWithValidToken(token)) {
return;
}
@@ -2256,8 +2255,7 @@
}
@BinderThread
- @Override
- public void reportStartInput(IBinder token, IBinder startInputToken) {
+ private void reportStartInput(IBinder token, IBinder startInputToken) {
if (!calledWithValidToken(token)) {
return;
}
@@ -3064,8 +3062,8 @@
}
}
- @Override
- public boolean shouldOfferSwitchingToNextInputMethod(IBinder token) {
+ @BinderThread
+ private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
if (!calledFromValidUser()) {
return false;
}
@@ -3148,13 +3146,21 @@
return;
}
+ /**
+ * This is kept due to {@link android.annotation.UnsupportedAppUsage} in
+ * {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in
+ * {@link InputMethodService#onCreate()}.
+ *
+ * <p>TODO(Bug 113914148): Check if we can remove this.</p>
+ * @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight()}
+ */
@Override
public int getInputMethodWindowVisibleHeight() {
return mWindowManagerInternal.getInputMethodWindowVisibleHeight();
}
- @Override
- public void clearLastInputMethodWindowForTransition(IBinder token) {
+ @BinderThread
+ private void clearLastInputMethodWindowForTransition(IBinder token) {
if (!calledFromValidUser()) {
return;
}
@@ -3216,8 +3222,8 @@
}
}
- @Override
- public void hideMySoftInput(IBinder token, int flags) {
+ @BinderThread
+ private void hideMySoftInput(@NonNull IBinder token, int flags) {
if (!calledFromValidUser()) {
return;
}
@@ -3234,8 +3240,8 @@
}
}
- @Override
- public void showMySoftInput(IBinder token, int flags) {
+ @BinderThread
+ private void showMySoftInput(@NonNull IBinder token, int flags) {
if (!calledFromValidUser()) {
return;
}
@@ -3349,11 +3355,13 @@
hideCurrentInputLocked(0, null);
}
return true;
- case MSG_ATTACH_TOKEN:
+ case MSG_INITIALIZE_IME:
args = (SomeArgs)msg.obj;
try {
if (DEBUG) Slog.v(TAG, "Sending attach of token: " + args.arg2);
- ((IInputMethod)args.arg1).attachToken((IBinder)args.arg2);
+ final IBinder token = (IBinder) args.arg2;
+ ((IInputMethod) args.arg1).initializeInternal(token,
+ new InputMethodPrivilegedOperationsImpl(this, token));
} catch (RemoteException e) {
}
args.recycle();
@@ -3396,15 +3404,6 @@
}
// ---------------------------------------------------------
- case MSG_ADD_CLIENT:
- addClient((ClientState) msg.obj);
- return true;
-
- case MSG_REMOVE_CLIENT:
- removeClient((IInputMethodClient) msg.obj);
- return true;
-
- // ---------------------------------------------------------
case MSG_UNBIND_CLIENT:
try {
@@ -4397,45 +4396,47 @@
private static final class LocalServiceImpl extends InputMethodManagerInternal {
@NonNull
- private final Handler mHandler;
+ private final InputMethodManagerService mService;
- LocalServiceImpl(@NonNull final Handler handler) {
- mHandler = handler;
+ LocalServiceImpl(@NonNull InputMethodManagerService service) {
+ mService = service;
}
@Override
public void addClient(IInputMethodClient client, IInputContext inputContext, int uid,
int pid) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_ADD_CLIENT,
- new ClientState(client, inputContext, uid, pid)));
+ // Work around Bug 113877122: We need to handle this synchronously. Otherwise, some
+ // IMM binder calls from the client process before we register this client.
+ mService.addClient(new ClientState(client, inputContext, uid, pid));
}
@Override
public void removeClient(IInputMethodClient client) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_CLIENT, client));
+ // Handle this synchronously to be consistent with addClient().
+ mService.removeClient(client);
}
@Override
public void setInteractive(boolean interactive) {
// Do everything in handler so as not to block the caller.
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_INTERACTIVE,
- interactive ? 1 : 0, 0));
+ mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
+ .sendToTarget();
}
@Override
public void hideCurrentInputMethod() {
- mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
- mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
+ mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
+ mService.mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
}
@Override
public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_START_VR_INPUT, componentName));
+ mService.mHandler.obtainMessage(MSG_START_VR_INPUT, componentName).sendToTarget();
}
}
- @Override
- public IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
+ @BinderThread
+ private IInputContentUriToken createInputContentUriToken(@Nullable IBinder token,
@Nullable Uri contentUri, @Nullable String packageName) {
if (!calledFromValidUser()) {
return null;
@@ -4492,8 +4493,8 @@
}
}
- @Override
- public void reportFullscreenMode(IBinder token, boolean fullscreen) {
+ @BinderThread
+ private void reportFullscreenMode(IBinder token, boolean fullscreen) {
if (!calledFromValidUser()) {
return;
}
@@ -4968,4 +4969,93 @@
return ShellCommandResult.SUCCESS;
}
}
+
+ private static final class InputMethodPrivilegedOperationsImpl
+ extends IInputMethodPrivilegedOperations.Stub {
+ private final InputMethodManagerService mImms;
+ private final IBinder mToken;
+ InputMethodPrivilegedOperationsImpl(InputMethodManagerService imms, IBinder token) {
+ mImms = imms;
+ mToken = token;
+ }
+
+ @BinderThread
+ @Override
+ public void setImeWindowStatus(int vis, int backDisposition) {
+ mImms.setImeWindowStatus(mToken, vis, backDisposition);
+ }
+
+ @BinderThread
+ @Override
+ public void reportStartInput(IBinder startInputToken) {
+ mImms.reportStartInput(mToken, startInputToken);
+ }
+
+ @BinderThread
+ @Override
+ public void clearLastInputMethodWindowForTransition() {
+ mImms.clearLastInputMethodWindowForTransition(mToken);
+ }
+
+ @BinderThread
+ @Override
+ public IInputContentUriToken createInputContentUriToken(Uri contentUri,
+ String packageName) {
+ return mImms.createInputContentUriToken(mToken, contentUri, packageName);
+ }
+
+ @BinderThread
+ @Override
+ public void reportFullscreenMode(boolean fullscreen) {
+ mImms.reportFullscreenMode(mToken, fullscreen);
+ }
+
+ @BinderThread
+ @Override
+ public void setInputMethod(String id) {
+ mImms.setInputMethod(mToken, id);
+ }
+
+ @BinderThread
+ @Override
+ public void setInputMethodAndSubtype(String id, InputMethodSubtype subtype) {
+ mImms.setInputMethodAndSubtype(mToken, id, subtype);
+ }
+
+ @BinderThread
+ @Override
+ public void hideMySoftInput(int flags) {
+ mImms.hideMySoftInput(mToken, flags);
+ }
+
+ @BinderThread
+ @Override
+ public void showMySoftInput(int flags) {
+ mImms.showMySoftInput(mToken, flags);
+ }
+
+ @BinderThread
+ @Override
+ public void updateStatusIcon(String packageName, @DrawableRes int iconId) {
+ mImms.updateStatusIcon(mToken, packageName, iconId);
+ }
+
+ @BinderThread
+ @Override
+ public boolean switchToPreviousInputMethod() {
+ return mImms.switchToPreviousInputMethod(mToken);
+ }
+
+ @BinderThread
+ @Override
+ public boolean switchToNextInputMethod(boolean onlyCurrentIme) {
+ return mImms.switchToNextInputMethod(mToken, onlyCurrentIme);
+ }
+
+ @BinderThread
+ @Override
+ public boolean shouldOfferSwitchingToNextInputMethod() {
+ return mImms.shouldOfferSwitchingToNextInputMethod(mToken);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java
new file mode 100644
index 0000000..ab7bf28
--- /dev/null
+++ b/services/core/java/com/android/server/LooperStatsService.java
@@ -0,0 +1,206 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.os.LooperStats;
+import com.android.internal.util.DumpUtils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @hide Only for use within the system server.
+ */
+public class LooperStatsService extends Binder {
+ private static final String TAG = "LooperStatsService";
+ private static final String LOOPER_STATS_SERVICE_NAME = "looper_stats";
+ private static final String SETTINGS_ENABLED_KEY = "enabled";
+ private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval";
+ private static final String DEBUG_SYS_LOOPER_STATS_ENABLED =
+ "debug.sys.looper_stats_enabled";
+ private static final int DEFAULT_SAMPLING_INTERVAL = 100;
+ private static final int DEFAULT_ENTRIES_SIZE_CAP = 2000;
+ private static final boolean DEFAULT_ENABLED = false;
+
+ private final Context mContext;
+ private final LooperStats mStats;
+ private boolean mEnabled = false;
+
+ private LooperStatsService(Context context, LooperStats stats) {
+ this.mContext = context;
+ this.mStats = stats;
+ }
+
+ private void initFromSettings() {
+ final KeyValueListParser parser = new KeyValueListParser(',');
+
+ try {
+ parser.setString(Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.LOOPER_STATS));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad looper_stats settings", e);
+ }
+
+ setSamplingInterval(
+ parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, DEFAULT_SAMPLING_INTERVAL));
+ // Manually specified value takes precedence over Settings.
+ setEnabled(SystemProperties.getBoolean(
+ DEBUG_SYS_LOOPER_STATS_ENABLED,
+ parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED)));
+ }
+
+ @Override
+ public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+ String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+ (new LooperShellCommand()).exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ List<LooperStats.ExportedEntry> entries = mStats.getEntries();
+ entries.sort(Comparator
+ .comparing((LooperStats.ExportedEntry entry) -> entry.threadName)
+ .thenComparing(entry -> entry.handlerClassName)
+ .thenComparing(entry -> entry.messageName));
+ String header = String.join(",", Arrays.asList(
+ "thread_name",
+ "handler_class",
+ "message_name",
+ "is_interactive",
+ "message_count",
+ "recorded_message_count",
+ "total_latency_micros",
+ "max_latency_micros",
+ "total_cpu_micros",
+ "max_cpu_micros",
+ "exception_count"));
+ pw.println(header);
+ for (LooperStats.ExportedEntry entry : entries) {
+ pw.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", entry.threadName,
+ entry.handlerClassName, entry.messageName, entry.isInteractive,
+ entry.messageCount, entry.recordedMessageCount, entry.totalLatencyMicros,
+ entry.maxLatencyMicros, entry.cpuUsageMicros, entry.maxCpuUsageMicros,
+ entry.exceptionCount);
+ }
+ }
+
+ private void setEnabled(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+ mStats.reset();
+ Looper.setObserver(enabled ? mStats : null);
+ }
+ }
+
+ private void setSamplingInterval(int samplingInterval) {
+ mStats.setSamplingInterval(samplingInterval);
+ }
+
+ /**
+ * Manages the lifecycle of LooperStatsService within System Server.
+ */
+ public static class Lifecycle extends SystemService {
+ private final SettingsObserver mSettingsObserver;
+ private final LooperStatsService mService;
+ private final LooperStats mStats;
+
+ public Lifecycle(Context context) {
+ super(context);
+ mStats = new LooperStats(DEFAULT_SAMPLING_INTERVAL, DEFAULT_ENTRIES_SIZE_CAP);
+ mService = new LooperStatsService(getContext(), mStats);
+ mSettingsObserver = new SettingsObserver(mService);
+ }
+
+ @Override
+ public void onStart() {
+ publishLocalService(LooperStats.class, mStats);
+ publishBinderService(LOOPER_STATS_SERVICE_NAME, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
+ mService.initFromSettings();
+ Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LOOPER_STATS);
+ getContext().getContentResolver().registerContentObserver(
+ settingsUri, false, mSettingsObserver, UserHandle.USER_SYSTEM);
+ mStats.setDeviceState(getLocalService(CachedDeviceState.Readonly.class));
+ }
+ }
+ }
+
+ private static class SettingsObserver extends ContentObserver {
+ private final LooperStatsService mService;
+
+ SettingsObserver(LooperStatsService service) {
+ super(BackgroundThread.getHandler());
+ mService = service;
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ mService.initFromSettings();
+ }
+ }
+
+ private class LooperShellCommand extends ShellCommand {
+ @Override
+ public int onCommand(String cmd) {
+ if ("enable".equals(cmd)) {
+ setEnabled(true);
+ return 0;
+ } else if ("disable".equals(cmd)) {
+ setEnabled(false);
+ return 0;
+ } else if ("reset".equals(cmd)) {
+ mStats.reset();
+ return 0;
+ } else {
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println(LOOPER_STATS_SERVICE_NAME + " commands:");
+ pw.println(" enable: Enable collecting stats");
+ pw.println(" disable: Disable collecting stats");
+ pw.println(" reset: Reset stats");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index d829602..daf870d 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -316,7 +316,6 @@
}
private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
- private volatile boolean mBandwidthControlEnabled;
private volatile boolean mFirewallEnabled;
private volatile boolean mStrictEnabled;
@@ -624,27 +623,11 @@
*/
private void prepareNativeDaemon() {
- mBandwidthControlEnabled = false;
-
- // only enable bandwidth control when support exists
- final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
-
// push any existing quota or UID rules
synchronized (mQuotaLock) {
- if (hasKernelSupport) {
- Slog.d(TAG, "enabling bandwidth control");
- try {
- mConnector.execute("bandwidth", "enable");
- mBandwidthControlEnabled = true;
- } catch (NativeDaemonConnectorException e) {
- Log.wtf(TAG, "problem enabling bandwidth controls", e);
- }
- } else {
- Slog.i(TAG, "not enabling bandwidth control");
- }
-
- SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
+ // Netd unconditionally enable bandwidth control
+ SystemProperties.set(PROP_QTAGUID_ENABLED, "1");
mStrictEnabled = true;
@@ -726,11 +709,10 @@
}
}
- if (mBandwidthControlEnabled) {
- try {
- getBatteryStats().noteNetworkStatsEnabled();
- } catch (RemoteException e) {
- }
+
+ try {
+ getBatteryStats().noteNetworkStatsEnabled();
+ } catch (RemoteException e) {
}
}
@@ -1576,10 +1558,6 @@
public void setInterfaceQuota(String iface, long quotaBytes) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- // silently discard when control disabled
- // TODO: eventually migrate to be always enabled
- if (!mBandwidthControlEnabled) return;
-
synchronized (mQuotaLock) {
if (mActiveQuotas.containsKey(iface)) {
throw new IllegalStateException("iface " + iface + " already has quota");
@@ -1610,10 +1588,6 @@
public void removeInterfaceQuota(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- // silently discard when control disabled
- // TODO: eventually migrate to be always enabled
- if (!mBandwidthControlEnabled) return;
-
synchronized (mQuotaLock) {
if (!mActiveQuotas.containsKey(iface)) {
// TODO: eventually consider throwing
@@ -1647,10 +1621,6 @@
public void setInterfaceAlert(String iface, long alertBytes) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- // silently discard when control disabled
- // TODO: eventually migrate to be always enabled
- if (!mBandwidthControlEnabled) return;
-
// quick sanity check
if (!mActiveQuotas.containsKey(iface)) {
throw new IllegalStateException("setting alert requires existing quota on iface");
@@ -1675,10 +1645,6 @@
public void removeInterfaceAlert(String iface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- // silently discard when control disabled
- // TODO: eventually migrate to be always enabled
- if (!mBandwidthControlEnabled) return;
-
synchronized (mQuotaLock) {
if (!mActiveAlerts.containsKey(iface)) {
// TODO: eventually consider throwing
@@ -1699,10 +1665,6 @@
public void setGlobalAlert(long alertBytes) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- // silently discard when control disabled
- // TODO: eventually migrate to be always enabled
- if (!mBandwidthControlEnabled) return;
-
try {
mConnector.execute("bandwidth", "setglobalalert", alertBytes);
} catch (NativeDaemonConnectorException e) {
@@ -1713,10 +1675,6 @@
private void setUidOnMeteredNetworkList(int uid, boolean blacklist, boolean enable) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- // silently discard when control disabled
- // TODO: eventually migrate to be always enabled
- if (!mBandwidthControlEnabled) return;
-
final String chain = blacklist ? "naughtyapps" : "niceapps";
final String suffix = enable ? "add" : "remove";
@@ -1868,7 +1826,7 @@
@Override
public boolean isBandwidthControlEnabled() {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- return mBandwidthControlEnabled;
+ return true;
}
@Override
@@ -2376,7 +2334,6 @@
mConnector.dump(fd, pw, args);
pw.println();
- pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
pw.print("mMobileActivityFromRadio="); pw.print(mMobileActivityFromRadio);
pw.print(" mLastPowerStateFromRadio="); pw.println(mLastPowerStateFromRadio);
pw.print("mNetworkActive="); pw.println(mNetworkActive);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 566ce4f..98b88cb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -50,6 +50,7 @@
import android.telephony.VoLteServiceState;
import android.util.LocalLog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.IPhoneStateListener;
@@ -82,7 +83,8 @@
* Eventually we may want to remove the notion of dummy value but for now this
* looks like the best approach.
*/
-class TelephonyRegistry extends ITelephonyRegistry.Stub {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private static final String TAG = "TelephonyRegistry";
private static final boolean DBG = false; // STOPSHIP if true
private static final boolean DBG_LOC = false; // STOPSHIP if true
@@ -99,6 +101,7 @@
IPhoneStateListener callback;
IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
+ IOnSubscriptionsChangedListener onOpportunisticSubscriptionsChangedListenerCallback;
int callerUid;
int callerPid;
@@ -117,6 +120,10 @@
return (onSubscriptionsChangedListenerCallback != null);
}
+ boolean matchOnOpportunisticSubscriptionsChangedListener() {
+ return (onOpportunisticSubscriptionsChangedListenerCallback != null);
+ }
+
boolean canReadCallLog() {
try {
return TelephonyPermissions.checkReadCallLog(
@@ -131,7 +138,9 @@
return "{callingPackage=" + callingPackage + " binder=" + binder
+ " callback=" + callback
+ " onSubscriptionsChangedListenererCallback="
- + onSubscriptionsChangedListenerCallback
+ + onSubscriptionsChangedListenerCallback
+ + " onOpportunisticSubscriptionsChangedListenererCallback="
+ + onOpportunisticSubscriptionsChangedListenerCallback
+ " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
+ " events=" + Integer.toHexString(events) + "}";
}
@@ -147,7 +156,9 @@
private final AppOpsManager mAppOps;
- private boolean hasNotifySubscriptionInfoChangedOccurred = false;
+ private boolean mHasNotifySubscriptionInfoChangedOccurred = false;
+
+ private boolean mHasNotifyOpportunisticSubscriptionInfoChangedOccurred = false;
private int mNumPhones;
@@ -315,7 +326,8 @@
// calls go through a oneway interface and local calls going through a
// handler before they get to app code.
- TelephonyRegistry(Context context) {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public TelephonyRegistry(Context context) {
CellLocation location = CellLocation.getEmpty();
mContext = context;
@@ -407,7 +419,7 @@
log("listen oscl: Register r=" + r);
}
// Always notify when registration occurs if there has been a notification.
- if (hasNotifySubscriptionInfoChangedOccurred) {
+ if (mHasNotifySubscriptionInfoChangedOccurred) {
try {
if (VDBG) log("listen oscl: send to r=" + r);
r.onSubscriptionsChangedListenerCallback.onSubscriptionsChanged();
@@ -417,7 +429,7 @@
remove(r.binder);
}
} else {
- log("listen oscl: hasNotifySubscriptionInfoChangedOccurred==false no callback");
+ log("listen oscl: mHasNotifySubscriptionInfoChangedOccurred==false no callback");
}
}
}
@@ -429,15 +441,61 @@
remove(callback.asBinder());
}
+
+ @Override
+ public void addOnOpportunisticSubscriptionsChangedListener(String callingPackage,
+ IOnSubscriptionsChangedListener callback) {
+ int callerUserId = UserHandle.getCallingUserId();
+ mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
+ if (VDBG) {
+ log("listen ooscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
+ + " callerUserId=" + callerUserId + " callback=" + callback
+ + " callback.asBinder=" + callback.asBinder());
+ }
+
+ synchronized (mRecords) {
+ // register
+ IBinder b = callback.asBinder();
+ Record r = add(b);
+
+ if (r == null) {
+ return;
+ }
+
+ r.context = mContext;
+ r.onOpportunisticSubscriptionsChangedListenerCallback = callback;
+ r.callingPackage = callingPackage;
+ r.callerUid = Binder.getCallingUid();
+ r.callerPid = Binder.getCallingPid();
+ r.events = 0;
+ if (DBG) {
+ log("listen ooscl: Register r=" + r);
+ }
+ // Always notify when registration occurs if there has been a notification.
+ if (mHasNotifyOpportunisticSubscriptionInfoChangedOccurred) {
+ try {
+ if (VDBG) log("listen ooscl: send to r=" + r);
+ r.onOpportunisticSubscriptionsChangedListenerCallback.onSubscriptionsChanged();
+ if (VDBG) log("listen ooscl: sent to r=" + r);
+ } catch (RemoteException e) {
+ if (VDBG) log("listen ooscl: remote exception sending to r=" + r + " e=" + e);
+ remove(r.binder);
+ }
+ } else {
+ log("listen ooscl: hasNotifyOpptSubInfoChangedOccurred==false no callback");
+ }
+ }
+ }
+
@Override
public void notifySubscriptionInfoChanged() {
if (VDBG) log("notifySubscriptionInfoChanged:");
synchronized (mRecords) {
- if (!hasNotifySubscriptionInfoChangedOccurred) {
+ if (!mHasNotifySubscriptionInfoChangedOccurred) {
log("notifySubscriptionInfoChanged: first invocation mRecords.size="
+ mRecords.size());
}
- hasNotifySubscriptionInfoChangedOccurred = true;
+ mHasNotifySubscriptionInfoChangedOccurred = true;
mRemoveList.clear();
for (Record r : mRecords) {
if (r.matchOnSubscriptionsChangedListener()) {
@@ -456,6 +514,33 @@
}
@Override
+ public void notifyOpportunisticSubscriptionInfoChanged() {
+ if (VDBG) log("notifyOpptSubscriptionInfoChanged:");
+ synchronized (mRecords) {
+ if (!mHasNotifyOpportunisticSubscriptionInfoChangedOccurred) {
+ log("notifyOpptSubscriptionInfoChanged: first invocation mRecords.size="
+ + mRecords.size());
+ }
+ mHasNotifyOpportunisticSubscriptionInfoChangedOccurred = true;
+ mRemoveList.clear();
+ for (Record r : mRecords) {
+ if (r.matchOnOpportunisticSubscriptionsChangedListener()) {
+ try {
+ if (VDBG) log("notifyOpptSubChanged: call oosc to r=" + r);
+ r.onOpportunisticSubscriptionsChangedListenerCallback
+ .onSubscriptionsChanged();
+ if (VDBG) log("notifyOpptSubChanged: done oosc to r=" + r);
+ } catch (RemoteException ex) {
+ if (VDBG) log("notifyOpptSubChanged: RemoteException r=" + r);
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
+ @Override
public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
boolean notifyNow) {
listenForSubscriber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, pkgForDebug, callback,
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 426a0c15..fd32b5a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5296,7 +5296,9 @@
try {
INotificationManager notificationManager = mInjector.getNotificationManager();
try {
- notificationManager.enqueueNotificationWithTag(packageName, packageName,
+ // The calling uid must match either the package or op package, so use an op
+ // package that matches the cleared calling identity.
+ notificationManager.enqueueNotificationWithTag(packageName, "android",
id.mTag, id.mId, notification, userId);
} catch (RemoteException e) {
/* ignore - local call */
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 3568a47..7276222 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -39,10 +39,13 @@
import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
+import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
import android.annotation.Nullable;
import android.app.ActivityOptions;
@@ -121,6 +124,8 @@
private DisplayWindowController mWindowContainerController;
+ private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+
@VisibleForTesting
ActivityDisplay(ActivityStackSupervisor supervisor, int displayId) {
this(supervisor, supervisor.mDisplayManager.getDisplay(displayId));
@@ -138,6 +143,10 @@
return new DisplayWindowController(mDisplay, this);
}
+ DisplayWindowController getWindowContainerController() {
+ return mWindowContainerController;
+ }
+
void updateBounds() {
mDisplay.getSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
@@ -442,6 +451,41 @@
}
/**
+ * Find task for putting the Activity in.
+ */
+ void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
+ FindTaskResult result) {
+ mTmpFindTaskResult.clear();
+ for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = getChildAt(stackNdx);
+ if (!r.hasCompatibleActivityType(stack)) {
+ if (DEBUG_TASKS) {
+ Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
+ }
+ continue;
+ }
+
+ stack.findTaskLocked(r, mTmpFindTaskResult);
+ // It is possible to have tasks in multiple stacks with the same root affinity, so
+ // we should keep looking after finding an affinity match to see if there is a
+ // better match in another stack. Also, task affinity isn't a good enough reason
+ // to target a display which isn't the source of the intent, so skip any affinity
+ // matches not on the specified display.
+ if (mTmpFindTaskResult.mRecord != null) {
+ if (mTmpFindTaskResult.mIdealMatch) {
+ result.setTo(mTmpFindTaskResult);
+ return;
+ } else if (isPreferredDisplay) {
+ // Note: since the traversing through the stacks is top down, the floating
+ // tasks should always have lower priority than any affinity-matching tasks
+ // in the fullscreen stacks
+ result.setTo(mTmpFindTaskResult);
+ }
+ }
+ }
+ }
+
+ /**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
@@ -837,7 +881,7 @@
if (mStacks.isEmpty() && mRemoved) {
mWindowContainerController.removeContainer();
mWindowContainerController = null;
- mSupervisor.releaseActivityDisplayLocked(mDisplayId);
+ mSupervisor.removeChild(this);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b91a449..2ee598f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4349,6 +4349,7 @@
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
+ final boolean clearLaunchStartTime = !restarting && app.removed && app.foregroundActivities;
boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1,
false /*replacingPid*/);
if (!kept && !restarting) {
@@ -4388,6 +4389,19 @@
} finally {
mWindowManager.continueSurfaceLayout();
}
+
+ // TODO (b/67683350)
+ // When an app process is removed, activities from the process may be relaunched. In the
+ // case of forceStopPackageLocked the activities are finished before any window is drawn,
+ // and the launch time is not cleared. This will be incorrectly used to calculate launch
+ // time for the next launched activity launched in the same windowing mode.
+ if (clearLaunchStartTime) {
+ final LaunchTimeTracker.Entry entry = mStackSupervisor
+ .getLaunchTimeTracker().getEntry(mStackSupervisor.getWindowingMode());
+ if (entry != null) {
+ entry.mLaunchStartTime = 0;
+ }
+ }
}
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
@@ -17061,7 +17075,7 @@
activeInstr.mUiAutomationConnection = uiAutomationConnection;
activeInstr.mResultClass = className;
- boolean disableHiddenApiChecks = ai.usesNonSdkApi
+ boolean disableHiddenApiChecks = ai.usesNonSdkApi()
|| (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
if (disableHiddenApiChecks) {
enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 9de6875..b24c36a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -302,7 +303,9 @@
mSamplingInterval = 0;
mAutoStop = false;
mStreaming = false;
- mUserId = defUser;
+ mUserId = mInternal.mUserController.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), defUser, false, ALLOW_FULL_ONLY,
+ "ActivityManagerShellCommand", null);
mDisplayId = INVALID_DISPLAY;
mWindowingMode = WINDOWING_MODE_UNDEFINED;
mActivityType = ACTIVITY_TYPE_UNDEFINED;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 78fef65..bc99827 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -775,6 +775,11 @@
true /* includingParents */);
}
+ void positionChildWindowContainerAtBottom(TaskRecord child) {
+ mWindowContainerController.positionChildAtBottom(child.getWindowContainerController(),
+ true /* includingParents */);
+ }
+
/**
* Returns whether to defer the scheduling of the multi-window mode.
*/
@@ -1171,8 +1176,8 @@
//dump();
if (DEBUG_TASKS) Slog.d(TAG_TASKS,
"For Intent " + intent + " bringing to top: " + r.intent);
- result.r = r;
- result.matchedByRootAffinity = false;
+ result.mRecord = r;
+ result.mIdealMatch = true;
break;
} else if (affinityIntent != null && affinityIntent.getComponent() != null &&
affinityIntent.getComponent().compareTo(cls) == 0 &&
@@ -1181,18 +1186,18 @@
//dump();
if (DEBUG_TASKS) Slog.d(TAG_TASKS,
"For Intent " + intent + " bringing to top: " + r.intent);
- result.r = r;
- result.matchedByRootAffinity = false;
+ result.mRecord = r;
+ result.mIdealMatch = true;
break;
} else if (!isDocument && !taskIsDocument
- && result.r == null && task.rootAffinity != null) {
+ && result.mRecord == null && task.rootAffinity != null) {
if (task.rootAffinity.equals(target.taskAffinity)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity candidate!");
// It is possible for multiple tasks to have the same root affinity especially
// if they are in separate stacks. We save off this candidate, but keep looking
// to see if there is a better candidate.
- result.r = r;
- result.matchedByRootAffinity = true;
+ result.mRecord = r;
+ result.mIdealMatch = false;
}
} else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
}
@@ -2859,8 +2864,7 @@
final int position = getAdjustedPositionForTask(task, mTaskHistory.size(), starting);
mTaskHistory.add(position, task);
updateTaskMovement(task, true);
- mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
- true /* includingParents */);
+ positionChildWindowContainerAtTop(task);
}
private void insertTaskAtBottom(TaskRecord task) {
@@ -2869,8 +2873,7 @@
final int position = getAdjustedPositionForTask(task, 0, null);
mTaskHistory.add(position, task);
updateTaskMovement(task, true);
- mWindowContainerController.positionChildAtBottom(task.getWindowContainerController(),
- true /* includingParents */);
+ positionChildWindowContainerAtBottom(task);
}
void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
@@ -3141,8 +3144,7 @@
p.reparent(targetTask, 0 /* position - bottom */, "resetTargetTaskIfNeeded");
}
- mWindowContainerController.positionChildAtBottom(
- targetTask.getWindowContainerController(), false /* includingParents */);
+ positionChildWindowContainerAtBottom(targetTask);
replyChainEnd = -1;
} else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
// If the activity should just be removed -- either
@@ -3277,8 +3279,7 @@
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Pulling activity " + p
+ " from " + srcPos + " in to resetting task " + task);
}
- mWindowContainerController.positionChildAtTop(
- task.getWindowContainerController(), true /* includingParents */);
+ positionChildWindowContainerAtTop(task);
// Now we've moved it in to place... but what if this is
// a singleTop activity and we have put it on top of another
@@ -5239,8 +5240,7 @@
addTask(task, toTop ? MAX_VALUE : 0, true /* schedulePictureInPictureModeChange */, reason);
if (toTop) {
// TODO: figure-out a way to remove this call.
- mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
- true /* includingParents */);
+ positionChildWindowContainerAtTop(task);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4cfcbee..af2d3b0 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -176,7 +176,10 @@
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.ActivityTaskManagerInternal.SleepToken;
import com.android.server.wm.ConfigurationContainer;
+import com.android.server.wm.DisplayWindowController;
import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.RootWindowContainerController;
+import com.android.server.wm.RootWindowContainerListener;
import com.android.server.wm.WindowManagerService;
import java.io.FileDescriptor;
@@ -190,7 +193,7 @@
import java.util.Set;
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
- RecentTasks.Callbacks {
+ RecentTasks.Callbacks, RootWindowContainerListener {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
private static final String TAG_IDLE = TAG + POSTFIX_IDLE;
@@ -416,9 +419,14 @@
/** Stack id of the front stack when user switched, indexed by userId. */
SparseIntArray mUserStackInFront = new SparseIntArray(2);
- // TODO: There should be an ActivityDisplayController coordinating am/wm interaction.
- /** Mapping from displayId to display current state */
- private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
+ /** Reference to default display so we can quickly look it up. */
+ private ActivityDisplay mDefaultDisplay;
+
+ /**
+ * List of displays which contain activities, sorted by z-order.
+ * The last entry in the list is the topmost.
+ */
+ private final ArrayList<ActivityDisplay> mActivityDisplays = new ArrayList<>();
private final SparseArray<IntArray> mDisplayAccessUIDs = new SparseArray<>();
@@ -453,7 +461,7 @@
@Override
protected ActivityDisplay getChildAt(int index) {
- return mActivityDisplays.valueAt(index);
+ return mActivityDisplays.get(index);
}
@Override
@@ -525,17 +533,21 @@
}
static class FindTaskResult {
- ActivityRecord r;
- boolean matchedByRootAffinity;
- }
- private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
+ ActivityRecord mRecord;
+ boolean mIdealMatch;
- /**
- * Temp storage for display ids sorted in focus order.
- * Maps position to id. Using {@link SparseIntArray} instead of {@link ArrayList} because
- * it's more efficient, as the number of displays is usually small.
- */
- private SparseIntArray mTmpOrderedDisplayIds = new SparseIntArray();
+ void clear() {
+ mRecord = null;
+ mIdealMatch = false;
+ }
+
+ void setTo(FindTaskResult result) {
+ mRecord = result.mRecord;
+ mIdealMatch = result.mIdealMatch;
+ }
+ }
+
+ private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
/**
* Used to keep track whether app visibilities got changed since the last pause. Useful to
@@ -569,6 +581,8 @@
private boolean mInitialized;
+ private RootWindowContainerController mWindowContainerController;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -612,6 +626,11 @@
mService = service;
}
+ @VisibleForTesting
+ void setWindowContainerController(RootWindowContainerController controller) {
+ mWindowContainerController = controller;
+ }
+
public void initialize() {
if (mInitialized) {
return;
@@ -664,38 +683,57 @@
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
getKeyguardController().setWindowManager(wm);
+ setWindowContainerController(new RootWindowContainerController(this));
- mDisplayManager =
- (DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
+ mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(this, null);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
- Display[] displays = mDisplayManager.getDisplays();
+ final Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = displays.length - 1; displayNdx >= 0; --displayNdx) {
final Display display = displays[displayNdx];
- ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
- mActivityDisplays.put(display.getDisplayId(), activityDisplay);
+ final ActivityDisplay activityDisplay = new ActivityDisplay(this, display);
+ if (activityDisplay.mDisplayId == DEFAULT_DISPLAY) {
+ mDefaultDisplay = activityDisplay;
+ }
+ addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
}
final ActivityDisplay defaultDisplay = getDefaultDisplay();
mHomeStack = mLastFocusedStack = defaultDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
+ }
+
+ /** Change the z-order of the given display. */
+ private void positionChildAt(ActivityDisplay display, int position) {
+ if (position >= mActivityDisplays.size()) {
+ position = mActivityDisplays.size() - 1;
+ } else if (position < 0) {
+ position = 0;
+ }
+
+ if (mActivityDisplays.isEmpty()) {
+ mActivityDisplays.add(display);
+ } else if (mActivityDisplays.get(position) != display) {
+ mActivityDisplays.remove(display);
+ mActivityDisplays.add(position, display);
+ }
+ }
+
+ @Override
+ public void onChildPositionChanged(DisplayWindowController childController, int position) {
+ // Assume AM lock is held from positionChildAt of controller in each hierarchy.
+ final ActivityDisplay display = getActivityDisplay(childController.getDisplayId());
+ if (display != null) {
+ positionChildAt(display, position);
+ }
}
ActivityStack getTopDisplayFocusedStack() {
- mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
-
- for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
- final int displayId = mTmpOrderedDisplayIds.get(i);
- final ActivityDisplay display = mActivityDisplays.get(displayId);
-
- // If WindowManagerService has encountered the display before we have, ignore as there
- // will be no stacks present and therefore no activities.
- if (display == null) {
- continue;
- }
- final ActivityStack focusedStack = display.getFocusedStack();
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityStack focusedStack = mActivityDisplays.get(i).getFocusedStack();
if (focusedStack != null) {
return focusedStack;
}
@@ -718,16 +756,8 @@
}
// The top focused stack might not have a resumed activity yet - look on all displays in
// focus order.
- mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
- for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
- final int displayId = mTmpOrderedDisplayIds.get(i);
- final ActivityDisplay display = mActivityDisplays.get(displayId);
-
- // If WindowManagerService has encountered the display before we have, ignore as there
- // will be no stacks present and therefore no activities.
- if (display == null) {
- continue;
- }
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
final ActivityRecord resumedActivityOnDisplay = display.getResumedActivity();
if (resumedActivityOnDisplay != null) {
return resumedActivityOnDisplay;
@@ -848,7 +878,7 @@
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
final TaskRecord task = stack.taskForIdLocked(id);
@@ -905,7 +935,7 @@
ActivityRecord isInAnyStackLocked(IBinder token) {
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
final ActivityRecord r = stack.isInStackLocked(token);
@@ -947,7 +977,7 @@
mWindowManager.deferSurfaceLayout();
try {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
final List<TaskRecord> tasks = stack.getAllTasks();
@@ -1011,7 +1041,7 @@
final String processName = app.processName;
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
if (!isTopDisplayFocusedStack(stack)) {
@@ -1046,7 +1076,7 @@
boolean allResumedActivitiesIdle() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
if (!isTopDisplayFocusedStack(stack) || stack.numActivities() == 0) {
@@ -1067,7 +1097,7 @@
boolean allResumedActivitiesComplete() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
if (isTopDisplayFocusedStack(stack)) {
@@ -1090,7 +1120,7 @@
private boolean allResumedActivitiesVisible() {
boolean foundResumed = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
final ActivityRecord r = stack.getResumedActivity();
@@ -1116,7 +1146,7 @@
boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- someActivityPaused |= mActivityDisplays.valueAt(displayNdx)
+ someActivityPaused |= mActivityDisplays.get(displayNdx)
.pauseBackStacks(userLeaving, resuming, dontWait);
}
return someActivityPaused;
@@ -1125,7 +1155,7 @@
boolean allPausedActivitiesComplete() {
boolean pausing = true;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
final ActivityRecord r = stack.mPausingActivity;
@@ -1145,7 +1175,7 @@
void cancelInitializingActivities() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.cancelInitializingActivities();
@@ -1266,17 +1296,8 @@
}
// Look in other non-focused and non-home stacks.
- mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
-
- for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
- final int displayId = mTmpOrderedDisplayIds.get(i);
- final ActivityDisplay display = mActivityDisplays.get(displayId);
-
- // If WindowManagerService has encountered the display before we have, ignore as there
- // will be no stacks present and therefore no activities.
- if (display == null) {
- continue;
- }
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
// TODO: We probably want to consider the top fullscreen stack as we could have a pinned
// stack on top.
@@ -1757,7 +1778,7 @@
boolean noResumedActivities = true;
boolean allFocusedProcessesDiffer = true;
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
final ActivityRecord resumedActivity = activityDisplay.getResumedActivity();
final WindowProcessController resumedActivityProcess =
resumedActivity == null ? null : resumedActivity.app;
@@ -1927,7 +1948,7 @@
void updateUIDsPresentOnDisplay() {
mDisplayAccessUIDs.clear();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
// Only bother calculating the whitelist for private displays
if (activityDisplay.isPrivate()) {
mDisplayAccessUIDs.append(
@@ -2173,7 +2194,7 @@
boolean handleAppDiedLocked(WindowProcessController app) {
boolean hasVisibleActivities = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
hasVisibleActivities |= stack.handleAppDiedLocked(app);
@@ -2184,7 +2205,7 @@
void closeSystemDialogsLocked() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.closeSystemDialogsLocked();
@@ -2213,7 +2234,7 @@
boolean doit, boolean evenPersistent, int userId) {
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
if (stack.finishDisabledPackageActivitiesLocked(
@@ -2235,7 +2256,7 @@
// hosted by the process that is actually still the foreground.
WindowProcessController fgApp = null;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
if (isTopDisplayFocusedStack(stack)) {
@@ -2277,7 +2298,7 @@
// Resume all top activities in focused stacks on all displays.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
final ActivityStack focusedStack = display.getFocusedStack();
if (focusedStack == null) {
continue;
@@ -2296,7 +2317,7 @@
void updateActivityApplicationInfoLocked(ApplicationInfo aInfo) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.updateActivityApplicationInfoLocked(aInfo);
@@ -2314,7 +2335,7 @@
TaskRecord finishedTask = null;
ActivityStack focusedStack = getTopDisplayFocusedStack();
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
// It is possible that request to finish activity might also remove its task and stack,
// so we need to be careful with indexes in the loop and check child count every time.
for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
@@ -2330,7 +2351,7 @@
void finishVoiceTask(IVoiceInteractionSession session) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
final int numStacks = display.getChildCount();
for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
@@ -2417,7 +2438,7 @@
protected <T extends ActivityStack> T getStack(int stackId) {
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final T stack = mActivityDisplays.valueAt(i).getStack(stackId);
+ final T stack = mActivityDisplays.get(i).getStack(stackId);
if (stack != null) {
return stack;
}
@@ -2428,7 +2449,7 @@
/** @see ActivityDisplay#getStack(int, int) */
private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final T stack = mActivityDisplays.valueAt(i).getStack(windowingMode, activityType);
+ final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
if (stack != null) {
return stack;
}
@@ -2642,19 +2663,12 @@
}
// Now look through all displays
- mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
- for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
- final int displayId = mTmpOrderedDisplayIds.get(i);
- if (displayId == preferredDisplay.mDisplayId) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ if (display == preferredDisplay) {
// We've already checked this one
continue;
}
- // If a display is registered in WM, it must also be available in AM.
- final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
- if (display == null) {
- // Looks like the display no longer exists in the system...
- continue;
- }
final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
ignoreCurrent);
if (nextFocusableStack != null) {
@@ -2676,13 +2690,12 @@
* @return Next valid {@link ActivityStack}, null if not found.
*/
ActivityStack getNextValidLaunchStackLocked(@NonNull ActivityRecord r, int currentFocus) {
- mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
- for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
- final int displayId = mTmpOrderedDisplayIds.get(i);
- if (displayId == currentFocus) {
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay display = mActivityDisplays.get(i);
+ if (display.mDisplayId == currentFocus) {
continue;
}
- final ActivityStack stack = getValidLaunchStackOnDisplay(displayId, r,
+ final ActivityStack stack = getValidLaunchStackOnDisplay(display.mDisplayId, r,
null /* options */);
if (stack != null) {
return stack;
@@ -3081,13 +3094,13 @@
*/
void removeStacksInWindowingModes(int... windowingModes) {
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- mActivityDisplays.valueAt(i).removeStacksInWindowingModes(windowingModes);
+ mActivityDisplays.get(i).removeStacksInWindowingModes(windowingModes);
}
}
void removeStacksWithActivityTypes(int... activityTypes) {
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- mActivityDisplays.valueAt(i).removeStacksWithActivityTypes(activityTypes);
+ mActivityDisplays.get(i).removeStacksWithActivityTypes(activityTypes);
}
}
@@ -3457,50 +3470,39 @@
return true;
}
- ActivityRecord findTaskLocked(ActivityRecord r, int displayId) {
- mTmpFindTaskResult.r = null;
- mTmpFindTaskResult.matchedByRootAffinity = false;
- ActivityRecord affinityMatch = null;
+ ActivityRecord findTaskLocked(ActivityRecord r, int preferredDisplayId) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (!r.hasCompatibleActivityType(stack)) {
- if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) "
- + stack);
- continue;
- }
- stack.findTaskLocked(r, mTmpFindTaskResult);
- // It is possible to have tasks in multiple stacks with the same root affinity, so
- // we should keep looking after finding an affinity match to see if there is a
- // better match in another stack. Also, task affinity isn't a good enough reason
- // to target a display which isn't the source of the intent, so skip any affinity
- // matches not on the specified display.
- if (mTmpFindTaskResult.r != null) {
- if (!mTmpFindTaskResult.matchedByRootAffinity) {
- return mTmpFindTaskResult.r;
- } else if (mTmpFindTaskResult.r.getDisplayId() == displayId) {
- // Note: since the traversing through the stacks is top down, the floating
- // tasks should always have lower priority than any affinity-matching tasks
- // in the fullscreen stacks
- affinityMatch = mTmpFindTaskResult.r;
- } else if (DEBUG_TASKS && mTmpFindTaskResult.matchedByRootAffinity) {
- Slog.d(TAG_TASKS, "Skipping match on different display "
- + mTmpFindTaskResult.r.getDisplayId() + " " + displayId);
- }
- }
+ mTmpFindTaskResult.clear();
+
+ // Looking up task on preferred display first
+ final ActivityDisplay preferredDisplay = getActivityDisplay(preferredDisplayId);
+ if (preferredDisplay != null) {
+ preferredDisplay.findTaskLocked(r, true /* isPreferredDisplay */, mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
}
}
- if (DEBUG_TASKS && affinityMatch == null) Slog.d(TAG_TASKS, "No task found");
- return affinityMatch;
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
+ if (display.mDisplayId == preferredDisplayId) {
+ continue;
+ }
+
+ display.findTaskLocked(r, false /* isPreferredDisplay */, mTmpFindTaskResult);
+ if (mTmpFindTaskResult.mIdealMatch) {
+ return mTmpFindTaskResult.mRecord;
+ }
+ }
+
+ if (DEBUG_TASKS && mTmpFindTaskResult.mRecord == null) Slog.d(TAG_TASKS, "No task found");
+ return mTmpFindTaskResult.mRecord;
}
ActivityRecord findActivityLocked(Intent intent, ActivityInfo info,
boolean compareIntentFilters) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
final ActivityRecord ar = stack.findActivityLocked(
@@ -3515,7 +3517,7 @@
boolean hasAwakeDisplay() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
if (!display.shouldSleep()) {
return true;
}
@@ -3543,7 +3545,7 @@
void prepareForShutdownLocked() {
for (int i = 0; i < mActivityDisplays.size(); i++) {
- createSleepTokenLocked("shutdown", mActivityDisplays.keyAt(i));
+ createSleepTokenLocked("shutdown", mActivityDisplays.get(i).mDisplayId);
}
}
@@ -3586,7 +3588,7 @@
void applySleepTokensLocked(boolean applyToStacks) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
// Set the sleeping state of the display.
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
final boolean displayShouldSleep = display.shouldSleep();
if (displayShouldSleep == display.isSleeping()) {
continue;
@@ -3666,7 +3668,7 @@
private boolean putStacksToSleepLocked(boolean allowDelay, boolean shuttingDown) {
boolean allSleep = true;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
if (allowDelay) {
@@ -3697,7 +3699,7 @@
void handleAppCrashLocked(WindowProcessController app) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.handleAppCrashLocked(app);
@@ -3746,7 +3748,7 @@
try {
// First the front stacks. In case any are not fullscreen and are in front of home.
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows,
@@ -3760,7 +3762,7 @@
void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.addStartingWindowsForVisibleActivities(taskSwitch);
@@ -3778,7 +3780,7 @@
}
mTaskLayersChanged = false;
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); displayNdx++) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
int baseLayer = 0;
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
@@ -3789,7 +3791,7 @@
void clearOtherAppTimeTrackers(AppTimeTracker except) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.clearOtherAppTimeTrackers(except);
@@ -3799,7 +3801,7 @@
void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.scheduleDestroyActivities(app, reason);
@@ -3818,7 +3820,7 @@
// let's iterate through the tasks and release the oldest one.
final int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
final int stackCount = display.getChildCount();
// Step through all stacks starting from behind, to hit the oldest things first.
for (int stackNdx = 0; stackNdx < stackCount; stackNdx++) {
@@ -3849,7 +3851,7 @@
mStartingUsers.add(uss);
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
stack.switchUserLocked(userId);
@@ -3953,7 +3955,7 @@
void validateTopActivitiesLocked() {
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
final ActivityRecord r = stack.topRunningActivityLocked();
@@ -3984,7 +3986,7 @@
public void dumpDisplays(PrintWriter pw) {
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.valueAt(i);
+ final ActivityDisplay display = mActivityDisplays.get(i);
pw.print("[id:" + display.mDisplayId + " stacks:");
display.dumpStacks(pw);
pw.print("]");
@@ -3998,7 +4000,7 @@
pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
- final ActivityDisplay display = mActivityDisplays.valueAt(i);
+ final ActivityDisplay display = mActivityDisplays.get(i);
display.dump(pw, prefix);
}
if (!mWaitingForActivityVisible.isEmpty()) {
@@ -4018,7 +4020,7 @@
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
activityDisplay.writeToProto(proto, DISPLAYS);
}
getKeyguardController().writeToProto(proto, KEYGUARD_CONTROLLER);
@@ -4047,7 +4049,7 @@
pw.print(prefix); pw.println("Display override configurations:");
final int displayCount = mActivityDisplays.size();
for (int i = 0; i < displayCount; i++) {
- final ActivityDisplay activityDisplay = mActivityDisplays.valueAt(i);
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
pw.print(prefix); pw.print(" "); pw.print(activityDisplay.mDisplayId); pw.print(": ");
pw.println(activityDisplay.getOverrideConfiguration());
}
@@ -4065,7 +4067,7 @@
ArrayList<ActivityRecord> activities = new ArrayList<>();
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
@@ -4096,11 +4098,11 @@
boolean dumpClient, String dumpPackage) {
boolean printed = false;
boolean needSep = false;
- for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ ActivityDisplay activityDisplay = mActivityDisplays.get(displayNdx);
pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
pw.println(" (activities from top to bottom):");
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
pw.println();
@@ -4299,12 +4301,18 @@
// TODO: Look into consolidating with getActivityDisplayOrCreateLocked()
ActivityDisplay getActivityDisplay(int displayId) {
- return mActivityDisplays.get(displayId);
+ for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+ final ActivityDisplay activityDisplay = mActivityDisplays.get(i);
+ if (activityDisplay.mDisplayId == displayId) {
+ return activityDisplay;
+ }
+ }
+ return null;
}
// TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
ActivityDisplay getDefaultDisplay() {
- return mActivityDisplays.get(DEFAULT_DISPLAY);
+ return mDefaultDisplay;
}
/**
@@ -4313,7 +4321,7 @@
*/
// TODO: Look into consolidating with getActivityDisplay()
ActivityDisplay getActivityDisplayOrCreateLocked(int displayId) {
- ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+ ActivityDisplay activityDisplay = getActivityDisplay(displayId);
if (activityDisplay != null) {
return activityDisplay;
}
@@ -4328,15 +4336,23 @@
}
// The display hasn't been added to ActivityManager yet, create a new record now.
activityDisplay = new ActivityDisplay(this, display);
- attachDisplay(activityDisplay);
+ addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
mWindowManager.onDisplayAdded(displayId);
return activityDisplay;
}
@VisibleForTesting
- void attachDisplay(ActivityDisplay display) {
- mActivityDisplays.put(display.mDisplayId, display);
+ void addChild(ActivityDisplay activityDisplay, int position) {
+ positionChildAt(activityDisplay, position);
+ mWindowContainerController.positionChildAt(
+ activityDisplay.getWindowContainerController(), position);
+ }
+
+ void removeChild(ActivityDisplay activityDisplay) {
+ // The caller must tell the controller of {@link ActivityDisplay} to release its container
+ // {@link DisplayContent}. That is done in {@link ActivityDisplay#releaseSelfIfNeeded}).
+ mActivityDisplays.remove(activityDisplay);
}
private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
@@ -4351,7 +4367,7 @@
}
synchronized (mService.mGlobalLock) {
- final ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+ final ActivityDisplay activityDisplay = getActivityDisplay(displayId);
if (activityDisplay == null) {
return;
}
@@ -4362,14 +4378,9 @@
}
}
- void releaseActivityDisplayLocked(int displayId) {
- mActivityDisplays.remove(displayId);
- }
-
-
private void handleDisplayChanged(int displayId) {
synchronized (mService.mGlobalLock) {
- ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
+ ActivityDisplay activityDisplay = getActivityDisplay(displayId);
// TODO: The following code block should be moved into {@link ActivityDisplay}.
if (activityDisplay != null) {
// The window policy is responsible for stopping activities on the default display
@@ -4392,7 +4403,7 @@
}
SleepToken createSleepTokenLocked(String tag, int displayId) {
- ActivityDisplay display = mActivityDisplays.get(displayId);
+ final ActivityDisplay display = getActivityDisplay(displayId);
if (display == null) {
throw new IllegalArgumentException("Invalid display: " + displayId);
}
@@ -4406,7 +4417,7 @@
private void removeSleepTokenLocked(SleepTokenImpl token) {
mSleepTokens.remove(token);
- ActivityDisplay display = mActivityDisplays.get(token.mDisplayId);
+ final ActivityDisplay display = getActivityDisplay(token.mDisplayId);
if (display != null) {
display.mAllSleepTokens.remove(token);
if (display.mAllSleepTokens.isEmpty()) {
@@ -4429,7 +4440,7 @@
private StackInfo getStackInfo(ActivityStack stack) {
final int displayId = stack.mDisplayId;
- final ActivityDisplay display = mActivityDisplays.get(displayId);
+ final ActivityDisplay display = getActivityDisplay(displayId);
StackInfo info = new StackInfo();
stack.getWindowContainerBounds(info.bounds);
info.displayId = displayId;
@@ -4483,7 +4494,7 @@
ArrayList<StackInfo> getAllStackInfosLocked() {
ArrayList<StackInfo> list = new ArrayList<>();
for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = mActivityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
list.add(getStackInfo(stack));
@@ -4765,14 +4776,12 @@
}
ActivityStack findStackBehind(ActivityStack stack) {
- // TODO(multi-display): We are only looking for stacks on the default display.
- final ActivityDisplay display = mActivityDisplays.get(DEFAULT_DISPLAY);
- if (display == null) {
- return null;
- }
- for (int i = display.getChildCount() - 1; i >= 0; i--) {
- if (display.getChildAt(i) == stack && i > 0) {
- return display.getChildAt(i - 1);
+ final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
+ if (display != null) {
+ for (int i = display.getChildCount() - 1; i >= 0; i--) {
+ if (display.getChildAt(i) == stack && i > 0) {
+ return display.getChildAt(i - 1);
+ }
}
}
throw new IllegalStateException("Failed to find a stack behind stack=" + stack
@@ -4904,7 +4913,7 @@
final ActivityStack topFocusedStack = getTopDisplayFocusedStack();
// Traverse all displays.
for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
- final ActivityDisplay display = mActivityDisplays.valueAt(i);
+ final ActivityDisplay display = mActivityDisplays.get(i);
// Traverse all stacks on a display.
for (int j = display.getChildCount() - 1; j >= 0; --j) {
final ActivityStack stack = display.getChildAt(j);
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 11f8bb1..7eadcb3 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -27,17 +27,7 @@
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.STOP_APP_SWITCHES;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.content.pm.PackageManager.FEATURE_PC;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
-import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
-import static android.provider.Settings.System.FONT_SCALE;
-import static com.android.server.am.ActivityManagerService.dumpStackTraces;
-import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
-import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
-import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_NONE;
@@ -51,15 +41,19 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
+import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Process.SYSTEM_UID;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
+import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
+import static android.provider.Settings.System.FONT_SCALE;
import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -68,6 +62,7 @@
import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
@@ -86,13 +81,13 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.am.ActivityManagerService.SEND_LOCALE_TO_MOUNT_DAEMON_MSG;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
import static com.android.server.am.ActivityManagerService.UPDATE_CONFIGURATION_MSG;
import static com.android.server.am.ActivityManagerService.checkComponentPermission;
+import static com.android.server.am.ActivityManagerService.dumpStackTraces;
import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
@@ -100,10 +95,16 @@
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.am.ActivityTaskManagerService.H.REPORT_TIME_TRACKER_MSG;
+import static com.android.server.am.ActivityTaskManagerService.UiHandler.DISMISS_DIALOG_UI_MSG;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -118,26 +119,8 @@
import android.app.ActivityTaskManager;
import android.app.ActivityThread;
import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.database.ContentObserver;
-import android.os.IUserManager;
-import android.os.PowerManager;
-import android.os.ServiceManager;
-import android.os.Trace;
-import android.os.UserManager;
-import android.os.WorkSource;
-import android.view.WindowManager;
-import com.android.internal.R;
-import com.android.internal.app.IAppOpsService;
-import com.android.server.AppOpsService;
-import com.android.server.SystemServiceManager;
-import com.android.server.pm.UserManagerService;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.AppGlobals;
+import android.app.Dialog;
import android.app.IActivityController;
import android.app.IActivityTaskManager;
import android.app.IApplicationThread;
@@ -157,15 +140,19 @@
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
@@ -177,16 +164,22 @@
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IUserManager;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
+import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UpdateLock;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.WorkSource;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
@@ -197,7 +190,6 @@
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
-
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.StatsLog;
@@ -206,22 +198,30 @@
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ProcessMap;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.util.Preconditions;
+import com.android.server.AppOpsService;
import com.android.server.AttributeCache;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
import com.android.server.Watchdog;
+import com.android.server.pm.UserManagerService;
+import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.WindowManagerService;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index bc23316..0a7e127 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -414,7 +414,11 @@
if (state == BroadcastRecord.IDLE) {
Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE");
}
- r.duration[r.nextReceiver - 1] = finishTime - r.receiverTime;
+ // If we're abandoning this broadcast before any receivers were actually spun up,
+ // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
+ if (r.nextReceiver > 0) {
+ r.duration[r.nextReceiver - 1] = finishTime - r.receiverTime;
+ }
r.receiver = null;
r.intent.setComponent(null);
if (r.curApp != null && r.curApp.curReceivers.contains(r)) {
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index f60c5c3..79c98e5 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -4,7 +4,6 @@
jsharkey@google.com
hackbod@google.com
omakoto@google.com
-fkupolov@google.com
ctate@google.com
huiyu@google.com
mwachens@google.com
@@ -28,7 +27,4 @@
michaelwr@google.com
narayan@google.com
-per-file GlobalSettingsToPropertiesMapper.java=fkupolov@google.com
-per-file GlobalSettingsToPropertiesMapper.java=omakoto@google.com
-per-file GlobalSettingsToPropertiesMapper.java=svetoslavganov@google.com
-per-file GlobalSettingsToPropertiesMapper.java=yamasani@google.com
+per-file GlobalSettingsToPropertiesMapper.java = omakoto@google.com, svetoslavganov@google.com, yamasani@google.com
diff --git a/services/core/java/com/android/server/am/RunningTasks.java b/services/core/java/com/android/server/am/RunningTasks.java
index 7008cee..d878f51 100644
--- a/services/core/java/com/android/server/am/RunningTasks.java
+++ b/services/core/java/com/android/server/am/RunningTasks.java
@@ -16,13 +16,9 @@
package com.android.server.am;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration.ActivityType;
import android.app.WindowConfiguration.WindowingMode;
-import android.util.SparseArray;
import java.util.ArrayList;
import java.util.Comparator;
@@ -45,7 +41,7 @@
private final ArrayList<TaskRecord> mTmpStackTasks = new ArrayList<>();
void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
- @WindowingMode int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
+ @WindowingMode int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
int callingUid, boolean allowed) {
// Return early if there are no tasks to fetch
if (maxNum <= 0) {
@@ -56,7 +52,7 @@
mTmpSortedSet.clear();
final int numDisplays = activityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
- final ActivityDisplay display = activityDisplays.valueAt(displayNdx);
+ final ActivityDisplay display = activityDisplays.get(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
mTmpStackTasks.clear();
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 4ca96a1..b817669 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -49,6 +49,20 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.am."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
],
"postsubmit": [
@@ -65,6 +79,14 @@
"include-filter": "com.android.server.am."
}
]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.am."
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index f211e17..8afac97 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -574,7 +574,8 @@
for (int i = 0; i < cryptoToken.length; i++) {
token.add(cryptoToken[i]);
}
- return daemon.enroll(token, timeout);
+ // TODO: plumb requireAttention down from framework
+ return daemon.enroll(token, timeout, true /* requireAttention */);
}
};
@@ -757,7 +758,7 @@
return 0;
}
try {
- return daemon.preEnroll().value;
+ return daemon.generateChallenge().value;
} catch (RemoteException e) {
Slog.e(TAG, "startPreEnroll failed", e);
}
@@ -771,7 +772,7 @@
return 0;
}
try {
- return daemon.postEnroll();
+ return daemon.revokeChallenge();
} catch (RemoteException e) {
Slog.e(TAG, "startPostEnroll failed", e);
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index c80c0f1..c028a43 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -26,6 +26,7 @@
import android.app.UriGrantsManager;
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -48,6 +49,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -630,8 +632,11 @@
// The default IME is always allowed to access the clipboard.
String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, UserHandle.getUserId(callingUid));
- if (defaultIme != null && defaultIme.equals(callingPackage)) {
- return true;
+ if (!TextUtils.isEmpty(defaultIme)) {
+ final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
+ if (imePkg.equals(callingPackage)) {
+ return true;
+ }
}
// Otherwise only focused applications can access the clipboard.
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index c473ef2..019d726 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -51,6 +51,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.RingBuffer;
@@ -72,7 +73,6 @@
import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
@@ -363,12 +363,9 @@
return;
}
- builder.setNightMode(mInjector.getSecureIntForUser(mContentResolver,
- Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0, UserHandle.USER_CURRENT)
- == 1);
- builder.setColorTemperature(mInjector.getSecureIntForUser(mContentResolver,
- Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
- 0, UserHandle.USER_CURRENT));
+ builder.setNightMode(mInjector.isNightModeActive(mContext, UserHandle.USER_CURRENT));
+ builder.setColorTemperature(mInjector.getColorTemperature(mContext,
+ UserHandle.USER_CURRENT));
BrightnessChangeEvent event = builder.build();
if (DEBUG) {
@@ -952,5 +949,13 @@
public boolean isInteractive(Context context) {
return context.getSystemService(PowerManager.class).isInteractive();
}
+
+ public int getColorTemperature(Context context, int userId) {
+ return new ColorDisplayController(context, userId).getColorTemperature();
+ }
+
+ public boolean isNightModeActive(Context context, int userId) {
+ return new ColorDisplayController(context, userId).isActivated();
+ }
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 9df9ba6..a050aa9 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,26 +16,10 @@
package com.android.server.input;
+import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
+
import android.annotation.NonNull;
-import android.os.LocaleList;
-import android.os.ShellCallback;
-import android.util.Log;
-import android.view.Display;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.R;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
-import com.android.server.DisplayThread;
-import com.android.server.LocalServices;
-import com.android.server.Watchdog;
-import com.android.server.policy.WindowManagerPolicy;
-
-import org.xmlpull.v1.XmlPullParser;
-
-import android.Manifest;
import android.app.IInputForwarder;
import android.app.Notification;
import android.app.NotificationManager;
@@ -50,8 +34,8 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
@@ -61,10 +45,10 @@
import android.hardware.display.DisplayViewport;
import android.hardware.input.IInputDevicesChangedListener;
import android.hardware.input.IInputManager;
+import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerInternal;
-import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
import android.os.Binder;
@@ -72,6 +56,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
@@ -81,6 +66,7 @@
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -97,6 +83,23 @@
import android.view.ViewConfiguration;
import android.widget.Toast;
+import com.android.internal.R;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import com.android.server.DisplayThread;
+import com.android.server.LocalServices;
+import com.android.server.Watchdog;
+import com.android.server.policy.WindowManagerPolicy;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import org.xmlpull.v1.XmlPullParser;
+
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
@@ -113,9 +116,6 @@
import java.util.Locale;
import java.util.Objects;
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
/*
* Wraps the C++ InputManager and provides its callbacks.
*/
@@ -212,7 +212,8 @@
int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
int policyFlags);
private static native void nativeToggleCapsLock(long ptr, int deviceId);
- private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
+ private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles,
+ 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 nativeSetFocusedApplication(long ptr,
@@ -292,11 +293,6 @@
/** Switch code: Camera lens cover. When set the lens is covered. */
public static final int SW_CAMERA_LENS_COVER = 0x09;
- // Viewport constants defined in InputReader.h.
- public static final int VIEWPORT_DEFAULT = 1;
- public static final int VIEWPORT_EXTERNAL = 2;
- public static final int VIEWPORT_VIRTUAL = 3;
-
public static final int SW_LID_BIT = 1 << SW_LID;
public static final int SW_TABLET_MODE_BIT = 1 << SW_TABLET_MODE;
public static final int SW_KEYPAD_SLIDE_BIT = 1 << SW_KEYPAD_SLIDE;
@@ -362,7 +358,7 @@
updateAccessibilityLargePointerFromSettings();
}
- // TODO(BT) Pass in paramter for bluetooth system
+ // TODO(BT) Pass in parameter for bluetooth system
public void systemRunning() {
if (DEBUG) {
Slog.d(TAG, "System ready.");
@@ -417,7 +413,7 @@
DisplayViewport externalTouchViewport,
List<DisplayViewport> virtualTouchViewports) {
if (defaultViewport.valid) {
- setDisplayViewport(VIEWPORT_DEFAULT, defaultViewport);
+ setDisplayViewport(VIEWPORT_INTERNAL, defaultViewport);
}
if (externalTouchViewport.valid) {
@@ -1467,7 +1463,7 @@
}
public void setInputWindows(InputWindowHandle[] windowHandles,
- InputWindowHandle focusedWindowHandle) {
+ InputWindowHandle focusedWindowHandle, int displayId) {
final IWindow newFocusedWindow =
focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
if (mFocusedWindow != newFocusedWindow) {
@@ -1476,7 +1472,7 @@
setPointerCapture(false);
}
}
- nativeSetInputWindows(mPtr, windowHandles);
+ nativeSetInputWindows(mPtr, windowHandles, displayId);
}
public void setFocusedApplication(InputApplicationHandle application) {
diff --git a/services/core/java/com/android/server/input/InputWindowHandle.java b/services/core/java/com/android/server/input/InputWindowHandle.java
index 720eaaa5..bb29bf8 100644
--- a/services/core/java/com/android/server/input/InputWindowHandle.java
+++ b/services/core/java/com/android/server/input/InputWindowHandle.java
@@ -17,8 +17,8 @@
package com.android.server.input;
import android.graphics.Region;
-import android.view.InputChannel;
import android.view.IWindow;
+import android.view.InputChannel;
/**
* Functions as a handle for a window that can receive input.
@@ -106,7 +106,7 @@
@Override
public String toString() {
- return new StringBuilder(name)
+ return new StringBuilder(name != null ? name : "")
.append(", layer=").append(layer)
.append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",")
.append(frameRight).append(",").append(frameBottom).append("]")
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index b57356f..b5a9f74 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -28,6 +28,10 @@
/**
* Called by the window manager service when a client process is being attached to the window
* manager service.
+ *
+ * <p>The caller must not have WindowManagerService lock. This method internally acquires
+ * InputMethodManagerService lock.</p>
+ *
* @param client {@link android.os.Binder} proxy that is associated with the singleton instance
* of {@link android.view.inputmethod.InputMethodManager} that runs on the client
* process
@@ -42,6 +46,10 @@
/**
* Called by the window manager service when a client process is being attached to the window
* manager service.
+ *
+ * <p>The caller must not have WindowManagerService lock. This method internally acquires
+ * InputMethodManagerService lock.</p>
+ *
* @param client {@link android.os.Binder} proxy that is associated with the singleton instance
* of {@link android.view.inputmethod.InputMethodManager} that runs on the client
* process
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 260633a..389782a 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -2413,6 +2413,8 @@
BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
(BatteryStatsInternal.class);
mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
+ StatsLog.write_non_chained(StatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
+ counter.numDeferred(), sinceLast);
}
}
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index a54811b..31cf9e3 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -69,7 +69,6 @@
import android.provider.Settings;
import android.provider.Telephony.Carriers;
import android.telephony.CarrierConfigManager;
-import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
@@ -1577,6 +1576,8 @@
mSingleShot = false;
native_stop();
mLastFixTime = 0;
+ // native_stop() may reset the position mode in hardware.
+ mLastPositionMode = null;
// reset SV count to zero
updateStatus(LocationProvider.TEMPORARILY_UNAVAILABLE);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ce71dd2..03b7652 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -35,6 +35,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import static android.content.pm.PackageManager.FEATURE_LEANBACK;
import static android.content.pm.PackageManager.FEATURE_TELEVISION;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
@@ -203,6 +205,7 @@
import com.android.server.lights.LightsManager;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
+import com.android.server.pm.PackageManagerService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -470,8 +473,8 @@
// Gather all notification listener components for candidate pkgs.
Set<ComponentName> approvedListeners =
mListeners.queryPackageForServices(whitelisted,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE, userId);
for (ComponentName cn : approvedListeners) {
try {
getBinderService().setNotificationListenerAccessGrantedForUser(cn,
@@ -507,8 +510,8 @@
// only be one
Set<ComponentName> approvedAssistants =
mAssistants.queryPackageForServices(defaultAssistantAccess,
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+ MATCH_DIRECT_BOOT_AWARE
+ | MATCH_DIRECT_BOOT_UNAWARE, userId);
for (ComponentName cn : approvedAssistants) {
try {
getBinderService().setNotificationAssistantAccessGrantedForUser(
@@ -1377,7 +1380,7 @@
NotificationUsageStats usageStats, AtomicFile policyFile,
ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm,
- IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal) {
+ IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps) {
Resources resources = getContext().getResources();
mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
@@ -1390,7 +1393,7 @@
mUgmInternal = ugmInternal;
mPackageManager = packageManager;
mPackageManagerClient = packageManagerClient;
- mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
+ mAppOps = appOps;
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mAppUsageStats = appUsageStats;
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -1544,7 +1547,8 @@
LocalServices.getService(UsageStatsManagerInternal.class),
LocalServices.getService(DevicePolicyManagerInternal.class),
UriGrantsManager.getService(),
- LocalServices.getService(UriGrantsManagerInternal.class));
+ LocalServices.getService(UriGrantsManagerInternal.class),
+ (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE));
// register for various Intents
IntentFilter filter = new IntentFilter();
@@ -2234,6 +2238,60 @@
}
@Override
+ public void setNotificationDelegate(String callingPkg, String delegate) {
+ checkCallerIsSameApp(callingPkg);
+ final int callingUid = Binder.getCallingUid();
+ UserHandle user = UserHandle.getUserHandleForUid(callingUid);
+ try {
+ ApplicationInfo info =
+ mPackageManager.getApplicationInfo(delegate,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ user.getIdentifier());
+ if (info != null) {
+ mPreferencesHelper.setNotificationDelegate(
+ callingPkg, callingUid, delegate, info.uid);
+ savePolicyFile();
+ }
+ } catch (RemoteException e) {
+ // :(
+ }
+ }
+
+ @Override
+ public void revokeNotificationDelegate(String callingPkg) {
+ checkCallerIsSameApp(callingPkg);
+ mPreferencesHelper.revokeNotificationDelegate(callingPkg, Binder.getCallingUid());
+ savePolicyFile();
+ }
+
+ @Override
+ public String getNotificationDelegate(String callingPkg) {
+ // callable by Settings also
+ checkCallerIsSystemOrSameApp(callingPkg);
+ return mPreferencesHelper.getNotificationDelegate(callingPkg, Binder.getCallingUid());
+ }
+
+ @Override
+ public boolean canNotifyAsPackage(String callingPkg, String targetPkg) {
+ checkCallerIsSameApp(callingPkg);
+ final int callingUid = Binder.getCallingUid();
+ UserHandle user = UserHandle.getUserHandleForUid(callingUid);
+ try {
+ ApplicationInfo info =
+ mPackageManager.getApplicationInfo(targetPkg,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ user.getIdentifier());
+ if (info != null) {
+ return mPreferencesHelper.isDelegateAllowed(
+ targetPkg, info.uid, callingPkg, callingUid);
+ }
+ } catch (RemoteException e) {
+ // :(
+ }
+ return false;
+ }
+
+ @Override
public void updateNotificationChannelGroupForPackage(String pkg, int uid,
NotificationChannelGroup group) throws RemoteException {
enforceSystemOrSystemUI("Caller not system or systemui");
@@ -4053,20 +4111,21 @@
Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
+ " notification=" + notification);
}
- checkCallerIsSystemOrSameApp(pkg);
- checkRestrictedCategories(notification);
-
- final int userId = ActivityManager.handleIncomingUser(callingPid,
- callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
- final UserHandle user = new UserHandle(userId);
if (pkg == null || notification == null) {
throw new IllegalArgumentException("null not allowed: pkg=" + pkg
+ " id=" + id + " notification=" + notification);
}
- // The system can post notifications for any package, let us resolve that.
- final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
+ final int userId = ActivityManager.handleIncomingUser(callingPid,
+ callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
+ final UserHandle user = UserHandle.of(userId);
+
+ // Can throw a SecurityException if the calling uid doesn't have permission to post
+ // as "pkg"
+ final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+
+ checkRestrictedCategories(notification);
// Fix the notification as best we can.
try {
@@ -4193,17 +4252,28 @@
}
}
- private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
- // The system can post notifications on behalf of any package it wants
- if (isCallerSystemOrPhone() && opPackageName != null && !"android".equals(opPackageName)) {
- try {
- return getContext().getPackageManager()
- .getPackageUidAsUser(opPackageName, userId);
- } catch (NameNotFoundException e) {
- /* ignore */
- }
+ @VisibleForTesting
+ int resolveNotificationUid(String callingPkg, String targetPkg,
+ int callingUid, int userId) {
+ // posted from app A on behalf of app A
+ if (isCallerSameApp(targetPkg, callingUid) && TextUtils.equals(callingPkg, targetPkg)) {
+ return callingUid;
}
- return callingUid;
+
+ int targetUid = -1;
+ try {
+ targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
+ } catch (NameNotFoundException e) {
+ /* ignore */
+ }
+ // posted from app A on behalf of app B
+ if (targetUid != -1 && (isCallerAndroid(callingPkg, callingUid)
+ || mPreferencesHelper.isDelegateAllowed(
+ targetPkg, targetUid, callingPkg, callingUid))) {
+ return targetUid;
+ }
+
+ throw new SecurityException("Caller " + callingUid + " cannot post for pkg " + targetPkg);
}
/**
@@ -4222,7 +4292,8 @@
// package or a registered listener can enqueue. Prevents DOS attacks and deals with leaks.
if (!isSystemNotification && !isNotificationFromListener) {
synchronized (mNotificationLock) {
- if (mNotificationsByKey.get(r.sbn.getKey()) == null && isCallerInstantApp(pkg)) {
+ if (mNotificationsByKey.get(r.sbn.getKey()) == null
+ && isCallerInstantApp(pkg, callingUid)) {
// Ephemeral apps have some special constraints for notifications.
// They are not allowed to create new notifications however they are allowed to
// update notifications created by the system (e.g. a foreground service
@@ -5149,11 +5220,11 @@
try {
Thread.sleep(waitMs);
} catch (InterruptedException e) { }
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
effect, "Notification (delayed)", record.getAudioAttributes());
}).start();
} else {
- mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(),
+ mVibrator.vibrate(record.sbn.getUid(), record.sbn.getPackageName(),
effect, "Notification", record.getAudioAttributes());
}
return true;
@@ -6282,6 +6353,11 @@
checkCallerIsSameApp(pkg);
}
+ private boolean isCallerAndroid(String callingPkg, int uid) {
+ return isUidSystemOrPhone(uid) && callingPkg != null
+ && PackageManagerService.PLATFORM_PACKAGE_NAME.equals(callingPkg);
+ }
+
/**
* Check if the notification is of a category type that is restricted to system use only,
* if so throw SecurityException
@@ -6302,13 +6378,13 @@
}
}
- private boolean isCallerInstantApp(String pkg) {
+ private boolean isCallerInstantApp(String pkg, int callingUid) {
// System is always allowed to act for ephemeral apps.
- if (isCallerSystemOrPhone()) {
+ if (isUidSystemOrPhone(callingUid)) {
return false;
}
- mAppOps.checkPackage(Binder.getCallingUid(), pkg);
+ mAppOps.checkPackage(callingUid, pkg);
try {
ApplicationInfo ai = mPackageManager.getApplicationInfo(pkg, 0,
@@ -6324,7 +6400,10 @@
}
private void checkCallerIsSameApp(String pkg) {
- final int uid = Binder.getCallingUid();
+ checkCallerIsSameApp(pkg, Binder.getCallingUid());
+ }
+
+ private void checkCallerIsSameApp(String pkg, int uid) {
try {
ApplicationInfo ai = mPackageManager.getApplicationInfo(
pkg, 0, UserHandle.getCallingUserId());
@@ -6340,6 +6419,24 @@
}
}
+ private boolean isCallerSameApp(String pkg) {
+ try {
+ checkCallerIsSameApp(pkg);
+ return true;
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+
+ private boolean isCallerSameApp(String pkg, int uid) {
+ try {
+ checkCallerIsSameApp(pkg, uid);
+ return true;
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+
private static String callStateToString(int state) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: return "CALL_STATE_IDLE";
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 432d17c..593e7cd 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
@@ -66,12 +67,14 @@
public class PreferencesHelper implements RankingConfig {
private static final String TAG = "NotificationPrefHelper";
private static final int XML_VERSION = 1;
+ private static final int UNKNOWN_UID = UserHandle.USER_NULL;
@VisibleForTesting
static final String TAG_RANKING = "ranking";
private static final String TAG_PACKAGE = "package";
private static final String TAG_CHANNEL = "channel";
private static final String TAG_GROUP = "channelGroup";
+ private static final String TAG_DELEGATE = "delegate";
private static final String ATT_VERSION = "version";
private static final String ATT_NAME = "name";
@@ -82,6 +85,8 @@
private static final String ATT_IMPORTANCE = "importance";
private static final String ATT_SHOW_BADGE = "show_badge";
private static final String ATT_APP_USER_LOCKED_FIELDS = "app_user_locked_fields";
+ private static final String ATT_ENABLED = "enabled";
+ private static final String ATT_USER_ALLOWED = "allowed";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
@@ -147,8 +152,7 @@
}
if (type == XmlPullParser.START_TAG) {
if (TAG_PACKAGE.equals(tag)) {
- int uid = XmlUtils.readIntAttribute(parser, ATT_UID,
- PackagePreferences.UNKNOWN_UID);
+ int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
String name = parser.getAttributeValue(null, ATT_NAME);
if (!TextUtils.isEmpty(name)) {
if (forRestore) {
@@ -217,6 +221,24 @@
r.channels.put(id, channel);
}
}
+ // Delegate
+ if (TAG_DELEGATE.equals(tagName)) {
+ int delegateId =
+ XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
+ String delegateName =
+ XmlUtils.readStringAttribute(parser, ATT_NAME);
+ boolean delegateEnabled = XmlUtils.readBooleanAttribute(
+ parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
+ boolean userAllowed = XmlUtils.readBooleanAttribute(
+ parser, ATT_USER_ALLOWED, Delegate.DEFAULT_USER_ALLOWED);
+ Delegate d = null;
+ if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(delegateName)) {
+ d = new Delegate(
+ delegateName, delegateId, delegateEnabled, userAllowed);
+ }
+ r.delegate = d;
+ }
+
}
try {
@@ -248,7 +270,7 @@
final String key = packagePreferencesKey(pkg, uid);
synchronized (mPackagePreferencess) {
PackagePreferences
- r = (uid == PackagePreferences.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
+ r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
: mPackagePreferencess.get(key);
if (r == null) {
r = new PackagePreferences();
@@ -265,7 +287,7 @@
Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
}
- if (r.uid == PackagePreferences.UNKNOWN_UID) {
+ if (r.uid == UNKNOWN_UID) {
mRestoredWithoutUids.put(pkg, r);
} else {
mPackagePreferencess.put(key, r);
@@ -357,7 +379,8 @@
|| r.showBadge != DEFAULT_SHOW_BADGE
|| r.lockedAppFields != DEFAULT_LOCKED_APP_FIELDS
|| r.channels.size() > 0
- || r.groups.size() > 0;
+ || r.groups.size() > 0
+ || r.delegate != null;
if (hasNonDefaultSettings) {
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, r.pkg);
@@ -378,6 +401,21 @@
out.attribute(null, ATT_UID, Integer.toString(r.uid));
}
+ if (r.delegate != null) {
+ out.startTag(null, TAG_DELEGATE);
+
+ out.attribute(null, ATT_NAME, r.delegate.mPkg);
+ out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
+ if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
+ out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
+ }
+ if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
+ out.attribute(null, ATT_USER_ALLOWED,
+ Boolean.toString(r.delegate.mUserAllowed));
+ }
+ out.endTag(null, TAG_DELEGATE);
+ }
+
for (NotificationChannelGroup group : r.groups.values()) {
group.writeXml(out);
}
@@ -923,16 +961,76 @@
* considered for sentiment adjustments (and thus never show a blocking helper).
*/
public void setAppImportanceLocked(String packageName, int uid) {
- PackagePreferences PackagePreferences = getOrCreatePackagePreferences(packageName, uid);
- if ((PackagePreferences.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
+ PackagePreferences prefs = getOrCreatePackagePreferences(packageName, uid);
+ if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
return;
}
- PackagePreferences.lockedAppFields =
- PackagePreferences.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
+ prefs.lockedAppFields = prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
updateConfig();
}
+ /**
+ * Returns the delegate for a given package, if it's allowed by the package and the user.
+ */
+ public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
+ PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+
+ if (prefs == null || prefs.delegate == null) {
+ return null;
+ }
+ if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
+ return null;
+ }
+ return prefs.delegate.mPkg;
+ }
+
+ /**
+ * Used by an app to delegate notification posting privileges to another apps.
+ */
+ public void setNotificationDelegate(String sourcePkg, int sourceUid,
+ String delegatePkg, int delegateUid) {
+ PackagePreferences prefs = getOrCreatePackagePreferences(sourcePkg, sourceUid);
+
+ boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
+ Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
+ prefs.delegate = delegate;
+ updateConfig();
+ }
+
+ /**
+ * Used by an app to turn off its notification delegate.
+ */
+ public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
+ PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+ if (prefs != null && prefs.delegate != null) {
+ prefs.delegate.mEnabled = false;
+ updateConfig();
+ }
+ }
+
+ /**
+ * Toggles whether an app can have a notification delegate on behalf of a user.
+ */
+ public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
+ PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+ if (prefs != null && prefs.delegate != null) {
+ prefs.delegate.mUserAllowed = userAllowed;
+ updateConfig();
+ }
+ }
+
+ /**
+ * Returns whether the given app is allowed on post notifications on behalf of the other given
+ * app.
+ */
+ public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
+ String potentialDelegatePkg, int potentialDelegateUid) {
+ PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+
+ return prefs != null && prefs.isValidDelegate(potentialDelegatePkg, potentialDelegateUid);
+ }
+
@VisibleForTesting
void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
if (original.canBypassDnd() != update.canBypassDnd()) {
@@ -994,8 +1092,7 @@
pw.print(" AppSettings: ");
pw.print(r.pkg);
pw.print(" (");
- pw.print(r.uid == PackagePreferences.UNKNOWN_UID ? "UNKNOWN_UID"
- : Integer.toString(r.uid));
+ pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid));
pw.print(')');
if (r.importance != DEFAULT_IMPORTANCE) {
pw.print(" importance=");
@@ -1356,8 +1453,6 @@
}
private static class PackagePreferences {
- static int UNKNOWN_UID = UserHandle.USER_NULL;
-
String pkg;
int uid = UNKNOWN_UID;
int importance = DEFAULT_IMPORTANCE;
@@ -1366,7 +1461,37 @@
boolean showBadge = DEFAULT_SHOW_BADGE;
int lockedAppFields = DEFAULT_LOCKED_APP_FIELDS;
+ Delegate delegate = null;
ArrayMap<String, NotificationChannel> channels = new ArrayMap<>();
Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>();
+
+ public boolean isValidDelegate(String pkg, int uid) {
+ return delegate != null && delegate.isAllowed(pkg, uid);
+ }
+ }
+
+ private static class Delegate {
+ static final boolean DEFAULT_ENABLED = true;
+ static final boolean DEFAULT_USER_ALLOWED = true;
+ String mPkg;
+ int mUid = UNKNOWN_UID;
+ boolean mEnabled = DEFAULT_ENABLED;
+ boolean mUserAllowed = DEFAULT_USER_ALLOWED;
+
+ Delegate(String pkg, int uid, boolean enabled, boolean userAllowed) {
+ mPkg = pkg;
+ mUid = uid;
+ mEnabled = enabled;
+ mUserAllowed = userAllowed;
+ }
+
+ public boolean isAllowed(String pkg, int uid) {
+ if (pkg == null || uid == UNKNOWN_UID) {
+ return false;
+ }
+ return pkg.equals(mPkg)
+ && uid == mUid
+ && (mUserAllowed && mEnabled);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index cf47d4e..bca3ca7 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -40,6 +40,7 @@
import java.io.FileDescriptor;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -124,7 +125,8 @@
synchronized (mPackageManagerService.mPackages) {
// Important: the packages we need to run with ab-ota compiler-reason.
important = PackageManagerServiceUtils.getPackagesForDexopt(
- mPackageManagerService.mPackages.values(), mPackageManagerService);
+ mPackageManagerService.mPackages.values(), mPackageManagerService,
+ DEBUG_DEXOPT);
// Others: we should optimize this with the (first-)boot compiler-reason.
others = new ArrayList<>(mPackageManagerService.mPackages.values());
others.removeAll(important);
@@ -157,6 +159,24 @@
long spaceAvailableNow = getAvailableSpace();
prepareMetricsLogging(important.size(), others.size(), spaceAvailable, spaceAvailableNow);
+
+ if (DEBUG_DEXOPT) {
+ try {
+ // Output some data about the packages.
+ PackageParser.Package lastUsed = Collections.max(important,
+ (pkg1, pkg2) -> Long.compare(
+ pkg1.getLatestForegroundPackageUseTimeInMills(),
+ pkg2.getLatestForegroundPackageUseTimeInMills()));
+ Log.d(TAG, "A/B OTA: lastUsed time = "
+ + lastUsed.getLatestForegroundPackageUseTimeInMills());
+ Log.d(TAG, "A/B OTA: deprioritized packages:");
+ for (PackageParser.Package pkg : others) {
+ Log.d(TAG, " " + pkg.packageName + " - "
+ + pkg.getLatestForegroundPackageUseTimeInMills());
+ }
+ } catch (Exception ignored) {
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9b097bf..cacdccb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1988,6 +1988,14 @@
mRequiredVerifierPackage, null /*finishedReceiver*/,
updateUserIds, instantUserIds);
}
+ // If package installer is defined, notify package installer about new
+ // app installed
+ if (mRequiredInstallerPackage != null) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
+ mRequiredInstallerPackage, null /*finishedReceiver*/,
+ firstUserIds, instantUserIds);
+ }
// Send replaced for users that don't see the package for the first time
if (update) {
@@ -8571,7 +8579,7 @@
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
- @GuardedBy("mInstallLock")
+ @GuardedBy({"mInstallLock", "mPackages"})
private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 1aea8f0..390c0cc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -164,6 +164,13 @@
public static List<PackageParser.Package> getPackagesForDexopt(
Collection<PackageParser.Package> packages,
PackageManagerService packageManagerService) {
+ return getPackagesForDexopt(packages, packageManagerService, DEBUG_DEXOPT);
+ }
+
+ public static List<PackageParser.Package> getPackagesForDexopt(
+ Collection<PackageParser.Package> packages,
+ PackageManagerService packageManagerService,
+ boolean debug) {
ArrayList<PackageParser.Package> remainingPkgs = new ArrayList<>(packages);
LinkedList<PackageParser.Package> result = new LinkedList<>();
ArrayList<PackageParser.Package> sortTemp = new ArrayList<>(remainingPkgs.size());
@@ -189,14 +196,14 @@
// TODO: add a property to control this?
Predicate<PackageParser.Package> remainingPredicate;
if (!remainingPkgs.isEmpty() && packageManagerService.isHistoricalPackageUsageAvailable()) {
- if (DEBUG_DEXOPT) {
+ if (debug) {
Log.i(TAG, "Looking at historical package use");
}
// Get the package that was used last.
PackageParser.Package lastUsed = Collections.max(remainingPkgs, (pkg1, pkg2) ->
Long.compare(pkg1.getLatestForegroundPackageUseTimeInMills(),
pkg2.getLatestForegroundPackageUseTimeInMills()));
- if (DEBUG_DEXOPT) {
+ if (debug) {
Log.i(TAG, "Taking package " + lastUsed.packageName + " as reference in time use");
}
long estimatedPreviousSystemUseTime =
@@ -218,7 +225,7 @@
applyPackageFilter(remainingPredicate, result, remainingPkgs, sortTemp,
packageManagerService);
- if (DEBUG_DEXOPT) {
+ if (debug) {
Log.i(TAG, "Packages to be dexopted: " + packagesToString(result));
Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgs));
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 91d4717..bdf12ca 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -334,9 +334,6 @@
// True if boot completed occurred. We keep the screen on until this happens.
private boolean mBootCompleted;
- // Runnables that should be triggered on boot completed
- private Runnable[] mBootCompletedRunnables;
-
// True if auto-suspend mode is enabled.
// Refer to autosuspend.h.
private boolean mHalAutoSuspendModeEnabled;
@@ -732,14 +729,6 @@
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
-
- if (!ArrayUtils.isEmpty(mBootCompletedRunnables)) {
- Slog.d(TAG, "Posting " + mBootCompletedRunnables.length + " delayed runnables");
- for (Runnable r : mBootCompletedRunnables) {
- BackgroundThread.getHandler().post(r);
- }
- }
- mBootCompletedRunnables = null;
}
}
}
@@ -954,16 +943,6 @@
mDirty |= DIRTY_SETTINGS;
}
- private void postAfterBootCompleted(Runnable r) {
- if (mBootCompleted) {
- BackgroundThread.getHandler().post(r);
- } else {
- Slog.d(TAG, "Delaying runnable until system is booted");
- mBootCompletedRunnables = ArrayUtils.appendElement(Runnable.class,
- mBootCompletedRunnables, r);
- }
- }
-
private void handleSettingsChangedLocked() {
updateSettingsLocked();
updatePowerStateLocked();
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 41c0be6..6f9d803 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -43,6 +43,7 @@
import android.os.IBinder;
import android.os.IStatsCompanionService;
import android.os.IStatsManager;
+import android.os.IStoraged;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
@@ -54,6 +55,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.telephony.ModemActivityInfo;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
@@ -70,14 +72,25 @@
import com.android.internal.os.KernelUidCpuTimeReader;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
+import com.android.internal.os.LooperStats;
import com.android.internal.os.PowerProfile;
import com.android.internal.util.DumpUtils;
import com.android.server.BinderCallsStatsService;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.storage.DiskStatsFileLogger;
+import com.android.server.storage.DiskStatsLoggingService;
+
+import libcore.io.IoUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -860,14 +873,6 @@
pulledData.add(e);
}
- private void pullDiskSpace(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 3);
- e.writeLong(mStatFsData.getAvailableBytes());
- e.writeLong(mStatFsSystem.getAvailableBytes());
- e.writeLong(mStatFsTemp.getAvailableBytes());
- pulledData.add(e);
- }
-
private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
e.writeLong(SystemClock.uptimeMillis());
@@ -938,6 +943,206 @@
}
}
+ private void pullLooperStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ LooperStats looperStats = LocalServices.getService(LooperStats.class);
+ if (looperStats == null) {
+ return;
+ }
+
+ List<LooperStats.ExportedEntry> entries = looperStats.getEntries();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ for (LooperStats.ExportedEntry entry : entries) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 9 /* fields */);
+ e.writeLong(0); // uid collection not implemented yet
+ e.writeString(entry.handlerClassName);
+ e.writeString(entry.threadName);
+ e.writeString(entry.messageName);
+ e.writeLong(entry.messageCount);
+ e.writeLong(entry.exceptionCount);
+ e.writeLong(entry.recordedMessageCount);
+ e.writeLong(entry.totalLatencyMicros);
+ e.writeLong(entry.cpuUsageMicros);
+ pulledData.add(e);
+ }
+ }
+
+ private void pullDiskStats(int tagId, List<StatsLogEventWrapper> pulledData) {
+ // Run a quick-and-dirty performance test: write 512 bytes
+ byte[] junk = new byte[512];
+ for (int i = 0; i < junk.length; i++) junk[i] = (byte) i; // Write nonzero bytes
+
+ File tmp = new File(Environment.getDataDirectory(), "system/statsdperftest.tmp");
+ FileOutputStream fos = null;
+ IOException error = null;
+
+ long before = SystemClock.elapsedRealtime();
+ try {
+ fos = new FileOutputStream(tmp);
+ fos.write(junk);
+ } catch (IOException e) {
+ error = e;
+ } finally {
+ try {
+ if (fos != null) fos.close();
+ } catch (IOException e) {
+ // Do nothing.
+ }
+ }
+
+ long latency = SystemClock.elapsedRealtime() - before;
+ if (tmp.exists()) tmp.delete();
+
+ if (error != null) {
+ Slog.e(TAG, "Error performing diskstats latency test");
+ latency = -1;
+ }
+ // File based encryption.
+ boolean fileBased = StorageManager.isFileEncryptedNativeOnly();
+
+ //Recent disk write speed. Binder call to storaged.
+ int writeSpeed = -1;
+ try {
+ IBinder binder = ServiceManager.getService("storaged");
+ if (binder == null) {
+ Slog.e(TAG, "storaged not found");
+ }
+ IStoraged storaged = IStoraged.Stub.asInterface(binder);
+ writeSpeed = storaged.getRecentPerf();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "storaged not found");
+ }
+
+ // Add info pulledData.
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeLong(latency);
+ e.writeBoolean(fileBased);
+ e.writeInt(writeSpeed);
+ pulledData.add(e);
+ }
+
+ private void pullDirectoryUsage(int tagId, List<StatsLogEventWrapper> pulledData) {
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ StatFs statFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
+ StatFs statFsSystem = new StatFs(Environment.getRootDirectory().getAbsolutePath());
+ StatFs statFsCache = new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__DATA);
+ e.writeLong(statFsData.getAvailableBytes());
+ e.writeLong(statFsData.getTotalBytes());
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__CACHE);
+ e.writeLong(statFsCache.getAvailableBytes());
+ e.writeLong(statFsCache.getTotalBytes());
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.DIRECTORY_USAGE__DIRECTORY__SYSTEM);
+ e.writeLong(statFsSystem.getAvailableBytes());
+ e.writeLong(statFsSystem.getTotalBytes());
+ pulledData.add(e);
+ }
+
+ private void pullAppSize(int tagId, List<StatsLogEventWrapper> pulledData) {
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ try {
+ String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
+ JSONObject json = new JSONObject(jsonStr);
+ long cache_time = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
+ JSONArray pkg_names = json.getJSONArray(DiskStatsFileLogger.PACKAGE_NAMES_KEY);
+ JSONArray app_sizes = json.getJSONArray(DiskStatsFileLogger.APP_SIZES_KEY);
+ JSONArray app_data_sizes = json.getJSONArray(DiskStatsFileLogger.APP_DATA_KEY);
+ JSONArray app_cache_sizes = json.getJSONArray(DiskStatsFileLogger.APP_CACHES_KEY);
+ // Sanity check: Ensure all 4 lists have the same length.
+ int length = pkg_names.length();
+ if (app_sizes.length() != length || app_data_sizes.length() != length
+ || app_cache_sizes.length() != length) {
+ Slog.e(TAG, "formatting error in diskstats cache file!");
+ return;
+ }
+ for (int i = 0; i < length; i++) {
+ StatsLogEventWrapper e =
+ new StatsLogEventWrapper(elapsedNanos, tagId, 5 /* fields */);
+ e.writeString(pkg_names.getString(i));
+ e.writeLong(app_sizes.optLong(i, -1L));
+ e.writeLong(app_data_sizes.optLong(i, -1L));
+ e.writeLong(app_cache_sizes.optLong(i, -1L));
+ e.writeLong(cache_time);
+ pulledData.add(e);
+ }
+ } catch (IOException | JSONException e) {
+ Slog.e(TAG, "exception reading diskstats cache file", e);
+ }
+ }
+
+ private void pullCategorySize(int tagId, List<StatsLogEventWrapper> pulledData) {
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
+ try {
+ String jsonStr = IoUtils.readFileAsString(DiskStatsLoggingService.DUMPSYS_CACHE_PATH);
+ JSONObject json = new JSONObject(jsonStr);
+ long cacheTime = json.optLong(DiskStatsFileLogger.LAST_QUERY_TIMESTAMP_KEY, -1L);
+
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_SIZE);
+ e.writeLong(json.optLong(DiskStatsFileLogger.APP_SIZE_AGG_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_DATA_SIZE);
+ e.writeLong(json.optLong(DiskStatsFileLogger.APP_DATA_SIZE_AGG_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__APP_CACHE_SIZE);
+ e.writeLong(json.optLong(DiskStatsFileLogger.APP_CACHE_AGG_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__PHOTOS);
+ e.writeLong(json.optLong(DiskStatsFileLogger.PHOTOS_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__VIDEOS);
+ e.writeLong(json.optLong(DiskStatsFileLogger.VIDEOS_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__AUDIO);
+ e.writeLong(json.optLong(DiskStatsFileLogger.AUDIO_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__DOWNLOADS);
+ e.writeLong(json.optLong(DiskStatsFileLogger.DOWNLOADS_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__SYSTEM);
+ e.writeLong(json.optLong(DiskStatsFileLogger.SYSTEM_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+
+ e = new StatsLogEventWrapper(elapsedNanos, tagId, 3 /* fields */);
+ e.writeInt(StatsLog.CATEGORY_SIZE__CATEGORY__OTHER);
+ e.writeLong(json.optLong(DiskStatsFileLogger.MISC_KEY, -1L));
+ e.writeLong(cacheTime);
+ pulledData.add(e);
+ } catch (IOException | JSONException e) {
+ Slog.e(TAG, "exception reading diskstats cache file", e);
+ }
+ }
+
/**
* Pulls various data.
*/
@@ -1012,10 +1217,6 @@
pullSystemElapsedRealtime(tagId, ret);
break;
}
- case StatsLog.DISK_SPACE: {
- pullDiskSpace(tagId, ret);
- break;
- }
case StatsLog.PROCESS_MEMORY_STATE: {
pullProcessMemoryState(tagId, ret);
break;
@@ -1028,6 +1229,26 @@
pullBinderCallsStatsExceptions(tagId, ret);
break;
}
+ case StatsLog.LOOPER_STATS: {
+ pullLooperStats(tagId, ret);
+ break;
+ }
+ case StatsLog.DISK_STATS: {
+ pullDiskStats(tagId, ret);
+ break;
+ }
+ case StatsLog.DIRECTORY_USAGE: {
+ pullDirectoryUsage(tagId, ret);
+ break;
+ }
+ case StatsLog.APP_SIZE: {
+ pullAppSize(tagId, ret);
+ break;
+ }
+ case StatsLog.CATEGORY_SIZE: {
+ pullCategorySize(tagId, ret);
+ break;
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c8bd211..479f427 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -55,7 +55,6 @@
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Color;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
@@ -99,7 +98,6 @@
import com.android.server.FgThread;
import com.android.server.SystemService;
-import java.lang.reflect.InvocationTargetException;
import libcore.io.IoUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -120,7 +118,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
-import com.android.internal.R;
public class WallpaperManagerService extends IWallpaperManager.Stub
implements IWallpaperManagerService {
@@ -1544,14 +1541,6 @@
return false;
}
- private Point getDefaultDisplaySize() {
- Point p = new Point();
- WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- Display d = wm.getDefaultDisplay();
- d.getRealSize(p);
- return p;
- }
-
public void setDimensionHints(int width, int height, String callingPackage)
throws RemoteException {
checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
@@ -1564,10 +1553,6 @@
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("width and height must be > 0");
}
- // Make sure it is at least as large as the display.
- Point displaySize = getDefaultDisplaySize();
- width = Math.max(width, displaySize.x);
- height = Math.max(height, displaySize.y);
if (width != wallpaper.width || height != wallpaper.height) {
wallpaper.width = width;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b775918..6da9f10 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -32,29 +32,11 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-
+import static android.view.WindowManager.TRANSIT_UNSET;
import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static android.view.WindowManager.TRANSIT_UNSET;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.AppWindowTokenProto.ALL_DRAWN;
import static com.android.server.wm.AppWindowTokenProto.APP_STOPPED;
import static com.android.server.wm.AppWindowTokenProto.CLIENT_HIDDEN;
@@ -78,6 +60,23 @@
import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW_VERBOSE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.logWithStack;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
@@ -1845,8 +1844,8 @@
surfaceInsets = win.getAttrs().surfaceInsets;
// XXX(b/72757033): These are insets relative to the window frame, but we're really
// interested in the insets relative to the frame we chose in the if-blocks above.
- insets.set(win.mContentInsets);
- stableInsets.set(win.mStableInsets);
+ win.getContentInsets(insets);
+ win.getStableInsets(stableInsets);
}
if (mLaunchTaskBehind) {
@@ -2099,7 +2098,7 @@
WindowState win = findMainWindow();
Rect appRect = win != null ? win.getContentFrameLw() :
new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
- Rect insets = win != null ? win.mContentInsets : null;
+ final Rect insets = win != null ? win.getContentInsets() : null;
final Configuration displayConfig = mDisplayContent.getConfiguration();
return mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(
appRect, insets, thumbnailHeader, getTask().mTaskId, displayConfig.uiMode,
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index a693071..731ebb8a 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -26,9 +26,9 @@
import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
-import android.os.Debug;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.Choreographer;
@@ -36,8 +36,8 @@
import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
-
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -178,7 +178,7 @@
BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
@SchedulePipModeChangedState int schedulePipModeChangedState,
@SchedulePipModeChangedState int prevShedulePipModeChangedState,
- boolean moveFromFullscreen, boolean moveToFullscreen) {
+ boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) {
super();
mTarget = target;
mFrom.set(from);
@@ -198,8 +198,8 @@
mFrozenTaskWidth = mTo.width();
mFrozenTaskHeight = mTo.height();
} else {
- mFrozenTaskWidth = mFrom.width();
- mFrozenTaskHeight = mFrom.height();
+ mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width();
+ mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height();
}
}
@@ -425,6 +425,7 @@
+ " schedulePipModeChangedState=" + schedulePipModeChangedState
+ " replacing=" + replacing);
+ Rect frozenTask = new Rect();
if (replacing) {
if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen)
&& (!moveFromFullscreen || existing.mMoveFromFullscreen)) {
@@ -467,12 +468,17 @@
moveFromFullscreen = existing.mMoveFromFullscreen;
}
+ // We are in the middle of an existing animation, so that this new animation may
+ // start from an interpolated bounds. We should keep using the existing frozen task
+ // width/height for consistent configurations.
+ frozenTask.set(0, 0, existing.mFrozenTaskWidth, existing.mFrozenTaskHeight);
+
// Since we are replacing, we skip both animation start and end callbacks
existing.cancel();
}
final BoundsAnimator animator = new BoundsAnimator(target, from, to,
schedulePipModeChangedState, prevSchedulePipModeChangedState,
- moveFromFullscreen, moveToFullscreen);
+ moveFromFullscreen, moveToFullscreen, frozenTask);
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
animator.setDuration((animationDuration != -1 ? animationDuration
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ba46737..d3e534c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -814,9 +814,7 @@
// {@link DisplayContent} ready for use.
mDisplayReady = true;
- // TODO(b/112081256): Use independent InputMonitor.
- mInputMonitor = isDefaultDisplay ? new InputMonitor(service, mDisplayId)
- : mService.getDefaultDisplayContentLocked().mInputMonitor;
+ mInputMonitor = new InputMonitor(service, mDisplayId);
}
boolean isReady() {
@@ -921,6 +919,11 @@
}
@Override
+ DisplayWindowController getController() {
+ return (DisplayWindowController) super.getController();
+ }
+
+ @Override
public Display getDisplay() {
return mDisplay;
}
@@ -1421,7 +1424,7 @@
config.densityDpi = displayInfo.logicalDensityDpi;
config.colorMode =
- (displayInfo.isHdr()
+ ((displayInfo.isHdr() && mService.hasHdrSupport())
? Configuration.COLOR_MODE_HDR_YES
: Configuration.COLOR_MODE_HDR_NO)
| (displayInfo.isWideColorGamut() && mService.hasWideColorGamutSupport()
@@ -2180,6 +2183,7 @@
mRemovingDisplay = false;
}
+ mInputMonitor.onRemoved();
mService.onDisplayRemoved(mDisplayId);
}
@@ -3004,7 +3008,7 @@
if (w.isVisibleLw() && (w.mAppToken != null || keyguard)) {
w.mWinAnimator.mDrawState = DRAW_PENDING;
// Force add to mResizingWindows.
- w.mLastContentInsets.set(-1, -1, -1, -1);
+ w.resetLastContentInsets();
mService.mWaitingForDrawn.add(w);
}
}, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index 74a8a35..76b6dbe 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -73,6 +73,10 @@
// override configuration propagation to just here.
}
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
/**
* Positions the task stack at the given position in the task stack container.
*/
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 6a08f4d..585a4f5 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -20,9 +20,9 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.view.Display;
import android.view.InputChannel;
import android.view.WindowManager;
+
import com.android.server.input.InputApplicationHandle;
import com.android.server.input.InputWindowHandle;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index c4beb55..3309798 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -367,6 +367,13 @@
}
}
+ void onRemoved() {
+ // If DisplayContent removed, we need find a way to remove window handles of this display
+ // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
+ mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
+ mDisplayId);
+ }
+
private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
InputConsumerImpl navInputConsumer;
InputConsumerImpl pipInputConsumer;
@@ -399,8 +406,7 @@
this.inDrag = inDrag;
wallpaperController = mService.mRoot.mWallpaperController;
- // TODO(b/112081256): Use independent InputMonitor for each display.
- mService.mRoot/*.getDisplayContent(mDisplayId)*/.forAllWindows(this,
+ mService.mRoot.getDisplayContent(mDisplayId).forAllWindows(this,
true /* traverseTopToBottom */);
if (mAddWallpaperInputConsumerHandle) {
// No visible wallpaper found, add the wallpaper input consumer at the end.
@@ -408,8 +414,8 @@
}
// Send windows to native code.
- // TODO: Update Input windows and focus by display?
- mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle);
+ mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
+ mDisplayId);
clearInputWindowHandlesLw();
@@ -429,7 +435,8 @@
final int flags = w.mAttrs.flags;
final int privateFlags = w.mAttrs.privateFlags;
final int type = w.mAttrs.type;
- final boolean hasFocus = w == mInputFocus;
+ // TODO(b/111361570): multi-display focus, one focus window per display.
+ final boolean hasFocus = w.isFocused();
final boolean isVisible = w.isVisibleLw();
if (mAddRecentsAnimationInputConsumerHandle) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 4dbd858..1eae567 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -373,7 +373,7 @@
: null;
final Rect contentInsets;
if (mTargetAppToken != null && mTargetAppToken.findMainWindow() != null) {
- contentInsets = mTargetAppToken.findMainWindow().mContentInsets;
+ contentInsets = mTargetAppToken.findMainWindow().getContentInsets();
} else {
// If the window for the activity had not yet been created, use the display insets.
mService.getStableInsets(mDisplayId, mTmpRect);
@@ -583,7 +583,8 @@
if (mainWindow == null) {
return null;
}
- final Rect insets = new Rect(mainWindow.mContentInsets);
+ final Rect insets = new Rect();
+ mainWindow.getContentInsets(insets);
InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
!topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 67ef471..00422e3 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -305,7 +305,8 @@
|| mCapturedLeash == null) {
return null;
}
- final Rect insets = new Rect(mainWindow.mContentInsets);
+ final Rect insets = new Rect();
+ mainWindow.getContentInsets(insets);
InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
mCapturedLeash, !mAppWindowToken.fillsParent(),
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d8cbb26..86b14337 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -174,24 +174,6 @@
return null;
}
- /**
- * Get an array with display ids ordered by focus priority - last items should be given
- * focus first. Sparse array just maps position to displayId.
- */
- void getDisplaysInFocusOrder(SparseIntArray displaysInFocusOrder) {
- displaysInFocusOrder.clear();
-
- final int size = mChildren.size();
- for (int i = 0; i < size; ++i) {
- final DisplayContent displayContent = mChildren.get(i);
- if (displayContent.isRemovalDeferred()) {
- // Don't report displays that are going to be removed soon.
- continue;
- }
- displaysInFocusOrder.put(i, displayContent.getDisplayId());
- }
- }
-
DisplayContent getDisplayContent(int displayId) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent current = mChildren.get(i);
@@ -1098,6 +1080,25 @@
}
@Override
+ void positionChildAt(int position, DisplayContent child, boolean includingParents) {
+ super.positionChildAt(position, child, includingParents);
+ final RootWindowContainerController controller = getController();
+ if (controller != null) {
+ controller.onChildPositionChanged(child, position);
+ }
+ }
+
+ void positionChildAt(int position, DisplayContent child) {
+ // Only called from controller so no need to notify the change to controller.
+ super.positionChildAt(position, child, false /* includingParents */);
+ }
+
+ @Override
+ RootWindowContainerController getController() {
+ return (RootWindowContainerController) super.getController();
+ }
+
+ @Override
void scheduleAnimation() {
mService.scheduleAnimationLocked();
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerController.java b/services/core/java/com/android/server/wm/RootWindowContainerController.java
new file mode 100644
index 0000000..93be6e9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootWindowContainerController.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+/**
+ * Controller for the root container. This is created by activity manager to link activity
+ * stack supervisor to the root window container they use in window manager.
+ */
+public class RootWindowContainerController
+ extends WindowContainerController<RootWindowContainer, RootWindowContainerListener> {
+
+ public RootWindowContainerController(RootWindowContainerListener listener) {
+ super(listener, WindowManagerService.getInstance());
+ synchronized (mWindowMap) {
+ mRoot.setController(this);
+ }
+ }
+
+ void onChildPositionChanged(DisplayContent child, int position) {
+ // This callback invokes to AM directly so here assumes AM lock is held. If there is another
+ // path called only with WM lock, it should change to use handler to post or move outside of
+ // WM lock with adding AM lock.
+ mListener.onChildPositionChanged(child.getController(), position);
+ }
+
+ /** Move the display to the given position. */
+ public void positionChildAt(DisplayWindowController child, int position) {
+ synchronized (mWindowMap) {
+ mContainer.positionChildAt(position, child.mContainer);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainerListener.java b/services/core/java/com/android/server/wm/RootWindowContainerListener.java
new file mode 100644
index 0000000..f413e3f7
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootWindowContainerListener.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Interface used by the creator of {@link RootWindowContainerController} to notify the changes to
+ * the display container in activity manager.
+ */
+public interface RootWindowContainerListener extends WindowContainerListener {
+ /** Called when the z-order of display is changed. */
+ void onChildPositionChanged(DisplayWindowController childController, int position);
+}
diff --git a/services/core/java/com/android/server/wm/TEST_MAPPING b/services/core/java/com/android/server/wm/TEST_MAPPING
index e885afa..c99329a 100644
--- a/services/core/java/com/android/server/wm/TEST_MAPPING
+++ b/services/core/java/com/android/server/wm/TEST_MAPPING
@@ -24,6 +24,20 @@
"exclude-annotation": "android.support.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.wm."
+ },
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ },
+ {
+ "exclude-annotation": "android.support.test.filters.FlakyTest"
+ }
+ ]
}
],
"postsubmit": [
@@ -37,6 +51,14 @@
"include-filter": "com.android.server.wm."
}
]
+ },
+ {
+ "name": "WmTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.wm."
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 71f34c9..eb419c9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -22,10 +22,8 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.res.Configuration.EMPTY;
+
import static com.android.server.EventLogTags.WM_TASK_REMOVED;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.TaskProto.APP_WINDOW_TOKENS;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
@@ -33,6 +31,9 @@
import static com.android.server.wm.TaskProto.ID;
import static com.android.server.wm.TaskProto.TEMP_INSET_BOUNDS;
import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.CallSuper;
import android.app.ActivityManager.TaskDescription;
@@ -43,8 +44,8 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import android.view.Surface;
-
import android.view.SurfaceControl;
+
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
@@ -472,7 +473,8 @@
void setDragResizing(boolean dragResizing, int dragResizeMode) {
if (mDragResizing != dragResizing) {
- if (!DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) {
+ // No need to check if the mode is allowed if it's leaving dragResize
+ if (dragResizing && !DragResizeMode.isModeAllowedForStack(mStack, dragResizeMode)) {
throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
+ mStack.mStackId + " dragResizeMode=" + dragResizeMode);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 6c8572a..b7507a4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -303,7 +303,7 @@
private Rect getInsets(WindowState state) {
// XXX(b/72757033): These are insets relative to the window frame, but we're really
// interested in the insets relative to the task bounds.
- final Rect insets = minRect(state.mContentInsets, state.mStableInsets);
+ final Rect insets = minRect(state.getContentInsets(), state.getStableInsets());
InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
return insets;
}
@@ -373,7 +373,7 @@
node.setClipToBounds(false);
final DisplayListCanvas c = node.start(width, height);
c.drawColor(color);
- decorPainter.setInsets(mainWindow.mContentInsets, mainWindow.mStableInsets);
+ decorPainter.setInsets(mainWindow.getContentInsets(), mainWindow.getStableInsets());
decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
node.end(c);
final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
@@ -383,7 +383,7 @@
// Note, the app theme snapshot is never translucent because we enforce a non-translucent
// color above
return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(),
- topChild.getConfiguration().orientation, mainWindow.mStableInsets,
+ topChild.getConfiguration().orientation, mainWindow.getStableInsets(),
ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */,
false /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
false);
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 5f41df7..9381fc6 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -18,23 +18,30 @@
import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
import static com.android.server.wm.WindowFramesProto.CONTENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.CONTENT_INSETS;
import static com.android.server.wm.WindowFramesProto.CUTOUT;
import static com.android.server.wm.WindowFramesProto.DECOR_FRAME;
import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
import static com.android.server.wm.WindowFramesProto.FRAME;
+import static com.android.server.wm.WindowFramesProto.OUTSETS;
import static com.android.server.wm.WindowFramesProto.OUTSET_FRAME;
import static com.android.server.wm.WindowFramesProto.OVERSCAN_FRAME;
+import static com.android.server.wm.WindowFramesProto.OVERSCAN_INSETS;
import static com.android.server.wm.WindowFramesProto.PARENT_FRAME;
+import static com.android.server.wm.WindowFramesProto.STABLE_INSETS;
import static com.android.server.wm.WindowFramesProto.VISIBLE_FRAME;
+import static com.android.server.wm.WindowFramesProto.VISIBLE_INSETS;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
+import android.view.DisplayCutout;
+import android.view.WindowManager;
+
+import com.android.server.wm.utils.InsetUtils;
+import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
-import android.view.DisplayCutout;
-
-import com.android.server.wm.utils.WmDisplayCutout;
/**
* Container class for all the window frames that affect how windows are laid out.
@@ -42,6 +49,7 @@
* TODO(b/111611553): Investigate which frames are still needed and which are duplicates
*/
public class WindowFrames {
+ private static final StringBuilder sTmpSB = new StringBuilder();
/**
* In most cases, this is the area of the entire screen.
@@ -59,7 +67,7 @@
*
* TODO(b/111611553): The name is unclear and most likely should be swapped with
* {@link #mParentFrame}
- */
+ */
public final Rect mDisplayFrame = new Rect();
/**
@@ -117,6 +125,12 @@
*/
final Rect mLastFrame = new Rect();
+ private boolean mFrameSizeChanged = false;
+
+ // Frame that is scaled to the application's coordinate space when in
+ // screen size compatibility mode.
+ final Rect mCompatFrame = new Rect();
+
/**
* Whether the parent frame would have been different if there was no display cutout.
*/
@@ -130,7 +144,52 @@
/**
* The last cutout that has been reported to the client.
*/
- WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+ private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+
+ private boolean mDisplayCutoutChanged;
+
+ /**
+ * Insets that determine the area covered by the display overscan region. These are in the
+ * application's coordinate space (without compatibility scale applied).
+ */
+ final Rect mOverscanInsets = new Rect();
+ final Rect mLastOverscanInsets = new Rect();
+ private boolean mOverscanInsetsChanged;
+
+ /**
+ * Insets that determine the area covered by the stable system windows. These are in the
+ * application's coordinate space (without compatibility scale applied).
+ */
+ final Rect mStableInsets = new Rect();
+ final Rect mLastStableInsets = new Rect();
+ private boolean mStableInsetsChanged;
+
+ /**
+ * Outsets determine the area outside of the surface where we want to pretend that it's possible
+ * to draw anyway.
+ */
+ final Rect mOutsets = new Rect();
+ final Rect mLastOutsets = new Rect();
+ private boolean mOutsetsChanged = false;
+
+ /**
+ * Insets that determine the actually visible area. These are in the application's
+ * coordinate space (without compatibility scale applied).
+ */
+ final Rect mVisibleInsets = new Rect();
+ final Rect mLastVisibleInsets = new Rect();
+ private boolean mVisibleInsetsChanged;
+
+ /**
+ * Insets that are covered by system windows (such as the status bar) and
+ * transient docking windows (such as the IME). These are in the application's
+ * coordinate space (without compatibility scale applied).
+ */
+ final Rect mContentInsets = new Rect();
+ final Rect mLastContentInsets = new Rect();
+ private boolean mContentInsetsChanged;
+
+ private final Rect mTmpRect = new Rect();
public WindowFrames() {
}
@@ -170,15 +229,141 @@
/**
* @return true if the width or height has changed since last reported to the client.
*/
- boolean didFrameSizeChange() {
+ private boolean didFrameSizeChange() {
return (mLastFrame.width() != mFrame.width()) || (mLastFrame.height() != mFrame.height());
}
/**
- * @return true if the display cutout has changed since last reported to the client.
+ * Calculates the outsets for this windowFrame. The outsets are calculated by the area between
+ * the {@link #mOutsetFrame} and the {@link #mContentFrame}. If there are no outsets, then
+ * {@link #mOutsets} is set to empty.
+ *
+ * @param hasOutsets Whether this frame has outsets.
*/
- boolean didDisplayCutoutChange() {
- return !mLastDisplayCutout.equals(mDisplayCutout);
+ void calculateOutsets(boolean hasOutsets) {
+ if (hasOutsets) {
+ InsetUtils.insetsBetweenFrames(mOutsetFrame, mContentFrame, mOutsets);
+ } else {
+ mOutsets.setEmpty();
+ }
+ }
+
+ /**
+ * Calculate the insets for the type {@link WindowManager.LayoutParams#TYPE_DOCK_DIVIDER}
+ *
+ * @param cutoutInsets The insets for the cutout.
+ */
+ void calculateDockedDividerInsets(Rect cutoutInsets) {
+ // For the docked divider, we calculate the stable insets like a full-screen window
+ // so it can use it to calculate the snap positions.
+ mTmpRect.set(mDisplayFrame);
+ mTmpRect.inset(cutoutInsets);
+ mTmpRect.intersectUnchecked(mStableFrame);
+ InsetUtils.insetsBetweenFrames(mDisplayFrame, mTmpRect, mStableInsets);
+
+ // The divider doesn't care about insets in any case, so set it to empty so we don't
+ // trigger a relayout when moving it.
+ mContentInsets.setEmpty();
+ mVisibleInsets.setEmpty();
+ mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
+ }
+
+ /**
+ * Calculate the insets for a window.
+ *
+ * @param windowsAreFloating Whether the window is in a floating task such as pinned or
+ * freeform
+ * @param inFullscreenContainer Whether the window is in a container that takes up the screen's
+ * entire space
+ * @param windowBounds The bounds for the window
+ */
+ void calculateInsets(boolean windowsAreFloating, boolean inFullscreenContainer,
+ Rect windowBounds) {
+ // Override right and/or bottom insets in case if the frame doesn't fit the screen in
+ // non-fullscreen mode.
+ boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
+ && mFrame.right > windowBounds.right;
+ boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
+ && mFrame.bottom > windowBounds.bottom;
+
+ mTmpRect.set(mFrame.left, mFrame.top, overrideRightInset ? mTmpRect.right : mFrame.right,
+ overrideBottomInset ? mTmpRect.bottom : mFrame.bottom);
+
+ InsetUtils.insetsBetweenFrames(mTmpRect, mContentFrame, mContentInsets);
+ InsetUtils.insetsBetweenFrames(mTmpRect, mVisibleFrame, mVisibleInsets);
+ InsetUtils.insetsBetweenFrames(mTmpRect, mStableFrame, mStableInsets);
+ }
+
+ /**
+ * Scales all the insets by a specific amount.
+ *
+ * @param scale The amount to scale the insets by.
+ */
+ void scaleInsets(float scale) {
+ mOverscanInsets.scale(scale);
+ mContentInsets.scale(scale);
+ mVisibleInsets.scale(scale);
+ mStableInsets.scale(scale);
+ mOutsets.scale(scale);
+ }
+
+ void offsetFrames(int layoutXDiff, int layoutYDiff) {
+ mFrame.offset(layoutXDiff, layoutYDiff);
+ mContentFrame.offset(layoutXDiff, layoutYDiff);
+ mVisibleFrame.offset(layoutXDiff, layoutYDiff);
+ mStableFrame.offset(layoutXDiff, layoutYDiff);
+ }
+
+ /**
+ * Updates info about whether the size of the window has changed since last reported.
+ *
+ * @return true if info about size has changed since last reported.
+ */
+ boolean setReportResizeHints() {
+ mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
+ mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
+ mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
+ mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
+ mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
+ mFrameSizeChanged |= didFrameSizeChange();
+ mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
+ return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
+ || mStableInsetsChanged || mOutsetsChanged || mFrameSizeChanged
+ || mDisplayCutoutChanged;
+ }
+
+ /**
+ * Resets the insets changed flags so they're all set to false again. This should be called
+ * after the insets are reported to client.
+ */
+ void resetInsetsChanged() {
+ mOverscanInsetsChanged = false;
+ mContentInsetsChanged = false;
+ mVisibleInsetsChanged = false;
+ mStableInsetsChanged = false;
+ mOutsetsChanged = false;
+ mFrameSizeChanged = false;
+ mDisplayCutoutChanged = false;
+ }
+
+ /**
+ * Copy over inset values as the last insets that were sent to the client.
+ */
+ void updateLastInsetValues() {
+ mLastOverscanInsets.set(mOverscanInsets);
+ mLastContentInsets.set(mContentInsets);
+ mLastVisibleInsets.set(mVisibleInsets);
+ mLastStableInsets.set(mStableInsets);
+ mLastOutsets.set(mOutsets);
+ mLastDisplayCutout = mDisplayCutout;
+ }
+
+ /**
+ * Sets the last content insets as (-1, -1, -1, -1) to force the next layout pass to update
+ * the client.
+ */
+ void resetLastContentInsets() {
+ mLastContentInsets.set(-1, -1, -1, -1);
}
public void writeToProto(@NonNull ProtoOutputStream proto, long fieldId) {
@@ -193,33 +378,57 @@
mContainingFrame.writeToProto(proto, CONTAINING_FRAME);
mFrame.writeToProto(proto, FRAME);
mDisplayCutout.getDisplayCutout().writeToProto(proto, CUTOUT);
+ mContentInsets.writeToProto(proto, CONTENT_INSETS);
+ mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
+ mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
+ mStableInsets.writeToProto(proto, STABLE_INSETS);
+ mOutsets.writeToProto(proto, OUTSETS);
+
proto.end(token);
}
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("Frames: containing=");
- mContainingFrame.printShortString(pw);
- pw.print(" parent="); mParentFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" display=");
- mDisplayFrame.printShortString(pw);
- pw.print(" overscan="); mOverscanFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" content=");
- mContentFrame.printShortString(pw);
- pw.print(" visible="); mVisibleFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" decor=");
- mDecorFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" outset=");
- mOutsetFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw);
- pw.print(" last="); mLastFrame.printShortString(pw);
- pw.println();
- pw.print(prefix); pw.print(" cutout=" + mDisplayCutout.getDisplayCutout());
- pw.print(" last=" + mLastDisplayCutout.getDisplayCutout());
- pw.println();
+ pw.println(prefix + "Frames: containing="
+ + mContainingFrame.toShortString(sTmpSB)
+ + " parent=" + mParentFrame.toShortString(sTmpSB));
+ pw.println(prefix + " display=" + mDisplayFrame.toShortString(sTmpSB)
+ + " overscan=" + mOverscanFrame.toShortString(sTmpSB));
+ pw.println(prefix + " content=" + mContentFrame.toShortString(sTmpSB)
+ + " visible=" + mVisibleFrame.toShortString(sTmpSB));
+ pw.println(prefix + " decor=" + mDecorFrame.toShortString(sTmpSB));
+ pw.println(prefix + " outset=" + mOutsetFrame.toShortString(sTmpSB));
+ pw.println(prefix + "mFrame=" + mFrame.toShortString(sTmpSB)
+ + " last=" + mLastFrame.toShortString(sTmpSB));
+ pw.println(prefix + " cutout=" + mDisplayCutout.getDisplayCutout()
+ + " last=" + mLastDisplayCutout.getDisplayCutout());
+ pw.print(prefix + "Cur insets: overscan=" + mOverscanInsets.toShortString(sTmpSB)
+ + " content=" + mContentInsets.toShortString(sTmpSB)
+ + " visible=" + mVisibleInsets.toShortString(sTmpSB)
+ + " stable=" + mStableInsets.toShortString(sTmpSB)
+ + " outsets=" + mOutsets.toShortString(sTmpSB));
+ pw.println(prefix + "Lst insets: overscan=" + mLastOverscanInsets.toShortString(sTmpSB)
+ + " content=" + mLastContentInsets.toShortString(sTmpSB)
+ + " visible=" + mLastVisibleInsets.toShortString(sTmpSB)
+ + " stable=" + mLastStableInsets.toShortString(sTmpSB)
+ + " outset=" + mLastOutsets.toShortString(sTmpSB));
+ }
+
+ String getInsetsInfo() {
+ return "ci=" + mContentInsets.toShortString()
+ + " vi=" + mVisibleInsets.toShortString()
+ + " si=" + mStableInsets.toShortString()
+ + " of=" + mOutsets.toShortString();
+ }
+
+ String getInsetsChangedInfo() {
+ return "contentInsetsChanged=" + mContentInsetsChanged
+ + " " + mContentInsets.toShortString()
+ + " visibleInsetsChanged=" + mVisibleInsetsChanged
+ + " " + mVisibleInsets.toShortString()
+ + " stableInsetsChanged=" + mStableInsetsChanged
+ + " " + mStableInsets.toShortString()
+ + " outsetsChanged=" + mOutsetsChanged
+ + " " + mOutsets.toShortString()
+ + " displayCutoutChanged=" + mDisplayCutoutChanged;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f8d0c72..679e0d8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -734,8 +734,9 @@
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
- // Indicates whether this device supports wide color gamut rendering
+ // Indicates whether this device supports wide color gamut / HDR rendering
private boolean mHasWideColorGamutSupport;
+ private boolean mHasHdrSupport;
// Who is holding the screen on.
private Session mHoldingScreenOn;
@@ -2167,14 +2168,10 @@
// The last inset values represent the last client state.
win.updateLastInsetValues();
- outFrame.set(win.mCompatFrame);
- outOverscanInsets.set(win.mOverscanInsets);
- outContentInsets.set(win.mContentInsets);
- win.mLastRelayoutContentInsets.set(win.mContentInsets);
- outVisibleInsets.set(win.mVisibleInsets);
- outStableInsets.set(win.mStableInsets);
+ win.getCompatFrame(outFrame);
+ win.getInsetsForRelayout(outOverscanInsets, outContentInsets, outVisibleInsets,
+ outStableInsets, outOutsets);
outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
- outOutsets.set(win.mOutsets);
outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
if (localLOGV) Slog.v(
TAG_WM, "Relayout given client " + client.asBinder()
@@ -4504,6 +4501,7 @@
mPolicy.systemReady();
mTaskSnapshotController.systemReady();
mHasWideColorGamutSupport = queryWideColorGamutSupport();
+ mHasHdrSupport = queryHdrSupport();
}
private static boolean queryWideColorGamutSupport() {
@@ -4519,6 +4517,19 @@
return false;
}
+ private static boolean queryHdrSupport() {
+ try {
+ ISurfaceFlingerConfigs surfaceFlinger = ISurfaceFlingerConfigs.getService();
+ OptionalBool hasHdr = surfaceFlinger.hasHDRDisplay();
+ if (hasHdr != null) {
+ return hasHdr.value;
+ }
+ } catch (RemoteException e) {
+ // Ignore, we're in big trouble if we can't talk to SurfaceFlinger's config store
+ }
+ return false;
+ }
+
// -------------------------------------------------------------
// Async Handler
// -------------------------------------------------------------
@@ -5345,17 +5356,6 @@
mWindowPlacerLocked.performSurfacePlacement();
}
- /**
- * Get an array with display ids ordered by focus priority - last items should be given
- * focus first. Sparse array just maps position to displayId.
- */
- // TODO: Maintain display list in focus order in ActivityManager and remove this call.
- public void getDisplaysInFocusOrder(SparseIntArray displaysInFocusOrder) {
- synchronized(mWindowMap) {
- mRoot.getDisplaysInFocusOrder(displaysInFocusOrder);
- }
- }
-
@Override
public void setOverscan(int displayId, int left, int top, int right, int bottom) {
if (mContext.checkCallingOrSelfPermission(
@@ -6004,11 +6004,10 @@
}
@Override
- public void createInputConsumer(IBinder token, String name, InputChannel inputChannel) {
+ public void createInputConsumer(IBinder token, String name, int displayId,
+ InputChannel inputChannel) {
synchronized (mWindowMap) {
- // TODO(b/112049699): Fix this for multiple displays. There is only one inputChannel
- // here to accept the return value.
- DisplayContent display = mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+ DisplayContent display = mRoot.getDisplayContent(displayId);
if (display != null) {
display.getInputMonitor().createInputConsumer(token, name, inputChannel,
Binder.getCallingPid(), Binder.getCallingUserHandle());
@@ -6017,11 +6016,9 @@
}
@Override
- public boolean destroyInputConsumer(String name) {
+ public boolean destroyInputConsumer(String name, int displayId) {
synchronized (mWindowMap) {
- // TODO(b/112049699): Fix this for multiple displays. For consistency with
- // createInputConsumer above.
- DisplayContent display = mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+ DisplayContent display = mRoot.getDisplayContent(displayId);
if (display != null) {
return display.getInputMonitor().destroyInputConsumer(name);
}
@@ -7510,6 +7507,10 @@
SystemProperties.getInt("persist.sys.sf.native_mode", 0) != 1;
}
+ boolean hasHdrSupport() {
+ return mHasHdrSupport && hasWideColorGamutSupport();
+ }
+
void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) {
if (!win.hideNonSystemOverlayWindowsWhenVisible()
&& !mHidingNonSystemOverlayWindows.contains(win)) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 97313f2..637c0ea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -120,7 +120,6 @@
import static com.android.server.wm.WindowStateProto.ANIMATOR;
import static com.android.server.wm.WindowStateProto.ATTRIBUTES;
import static com.android.server.wm.WindowStateProto.CHILD_WINDOWS;
-import static com.android.server.wm.WindowStateProto.CONTENT_INSETS;
import static com.android.server.wm.WindowStateProto.DESTROYING;
import static com.android.server.wm.WindowStateProto.DISPLAY_ID;
import static com.android.server.wm.WindowStateProto.FINISHED_SEAMLESS_ROTATION_FRAME;
@@ -131,20 +130,16 @@
import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
-import static com.android.server.wm.WindowStateProto.OUTSETS;
-import static com.android.server.wm.WindowStateProto.OVERSCAN_INSETS;
import static com.android.server.wm.WindowStateProto.PENDING_SEAMLESS_ROTATION;
import static com.android.server.wm.WindowStateProto.REMOVED;
import static com.android.server.wm.WindowStateProto.REMOVE_ON_EXIT;
import static com.android.server.wm.WindowStateProto.REQUESTED_HEIGHT;
import static com.android.server.wm.WindowStateProto.REQUESTED_WIDTH;
-import static com.android.server.wm.WindowStateProto.STABLE_INSETS;
import static com.android.server.wm.WindowStateProto.STACK_ID;
import static com.android.server.wm.WindowStateProto.SURFACE_INSETS;
import static com.android.server.wm.WindowStateProto.SURFACE_POSITION;
import static com.android.server.wm.WindowStateProto.SYSTEM_UI_VISIBILITY;
import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY;
-import static com.android.server.wm.WindowStateProto.VISIBLE_INSETS;
import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER;
import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES;
@@ -200,6 +195,7 @@
import com.android.server.input.InputWindowHandle;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import com.android.server.wm.utils.InsetUtils;
import com.android.server.wm.utils.WmDisplayCutout;
import java.io.PrintWriter;
@@ -309,22 +305,6 @@
private final MergedConfiguration mLastReportedConfiguration = new MergedConfiguration();
/**
- * Insets that determine the actually visible area. These are in the application's
- * coordinate space (without compatibility scale applied).
- */
- final Rect mVisibleInsets = new Rect();
- private final Rect mLastVisibleInsets = new Rect();
- private boolean mVisibleInsetsChanged;
-
- /**
- * Insets that are covered by system windows (such as the status bar) and
- * transient docking windows (such as the IME). These are in the application's
- * coordinate space (without compatibility scale applied).
- */
- final Rect mContentInsets = new Rect();
- final Rect mLastContentInsets = new Rect();
-
- /**
* The last content insets returned to the client in relayout. We use
* these in the bounds animation to ensure we only observe inset changes
* at the same time that a client resizes it's surface so that we may use
@@ -333,34 +313,6 @@
*/
final Rect mLastRelayoutContentInsets = new Rect();
- private boolean mContentInsetsChanged;
-
- /**
- * Insets that determine the area covered by the display overscan region. These are in the
- * application's coordinate space (without compatibility scale applied).
- */
- final Rect mOverscanInsets = new Rect();
- private final Rect mLastOverscanInsets = new Rect();
- private boolean mOverscanInsetsChanged;
-
- /**
- * Insets that determine the area covered by the stable system windows. These are in the
- * application's coordinate space (without compatibility scale applied).
- */
- final Rect mStableInsets = new Rect();
- private final Rect mLastStableInsets = new Rect();
- private boolean mStableInsetsChanged;
-
- /**
- * Outsets determine the area outside of the surface where we want to pretend that it's possible
- * to draw anyway.
- */
- final Rect mOutsets = new Rect();
- private final Rect mLastOutsets = new Rect();
- private boolean mOutsetsChanged = false;
-
- private boolean mDisplayCutoutChanged;
-
/**
* Set to true if we are waiting for this window to receive its
* given internal insets before laying out other windows based on it.
@@ -399,11 +351,6 @@
float mLastHScale=1, mLastVScale=1;
final Matrix mTmpMatrix = new Matrix();
- private boolean mFrameSizeChanged = false;
- // Frame that is scaled to the application's coordinate space when in
- // screen size compatibility mode.
- final Rect mCompatFrame = new Rect();
-
private final WindowFrames mWindowFrames = new WindowFrames();
/**
@@ -602,6 +549,8 @@
*/
private long mFrameNumber = -1;
+ private static final StringBuilder sTmpSB = new StringBuilder();
+
/**
* Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
* of z-order and 1 otherwise.
@@ -986,17 +935,7 @@
applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
// Calculate the outsets before the content frame gets shrinked to the window frame.
- if (hasOutsets) {
- mOutsets.set(
- Math.max(mWindowFrames.mContentFrame.left - mWindowFrames.mOutsetFrame.left, 0),
- Math.max(mWindowFrames.mContentFrame.top - mWindowFrames.mOutsetFrame.top, 0),
- Math.max(mWindowFrames.mOutsetFrame.right - mWindowFrames.mContentFrame.right,
- 0),
- Math.max(mWindowFrames.mOutsetFrame.bottom - mWindowFrames.mContentFrame.bottom,
- 0));
- } else {
- mOutsets.set(0, 0, 0, 0);
- }
+ mWindowFrames.calculateOutsets(hasOutsets);
// Make sure the content and visible frames are inside of the
// final window frame.
@@ -1053,91 +992,35 @@
if (inFullscreenContainer && !windowsAreFloating) {
// Windows that are not fullscreen can be positioned outside of the display frame,
// but that is not a reason to provide them with overscan insets.
- mOverscanInsets.set(
- Math.max(mWindowFrames.mOverscanFrame.left - layoutContainingFrame.left, 0),
- Math.max(mWindowFrames.mOverscanFrame.top - layoutContainingFrame.top, 0),
- Math.max(layoutContainingFrame.right - mWindowFrames.mOverscanFrame.right, 0),
- Math.max(layoutContainingFrame.bottom - mWindowFrames.mOverscanFrame.bottom,
- 0));
+ InsetUtils.insetsBetweenFrames(layoutContainingFrame, mWindowFrames.mOverscanFrame,
+ mWindowFrames.mOverscanInsets);
}
if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- // For the docked divider, we calculate the stable insets like a full-screen window
- // so it can use it to calculate the snap positions.
final WmDisplayCutout c = windowFrames.mDisplayCutout.calculateRelativeTo(
mWindowFrames.mDisplayFrame);
- mTmpRect.set(mWindowFrames.mDisplayFrame);
- mTmpRect.inset(c.getDisplayCutout().getSafeInsets());
- mTmpRect.intersectUnchecked(mWindowFrames.mStableFrame);
-
- mStableInsets.set(Math.max(mTmpRect.left - mWindowFrames.mDisplayFrame.left, 0),
- Math.max(mTmpRect.top - mWindowFrames.mDisplayFrame.top, 0),
- Math.max(mWindowFrames.mDisplayFrame.right - mTmpRect.right, 0),
- Math.max(mWindowFrames.mDisplayFrame.bottom - mTmpRect.bottom, 0));
-
- // The divider doesn't care about insets in any case, so set it to empty so we don't
- // trigger a relayout when moving it.
- mContentInsets.setEmpty();
- mVisibleInsets.setEmpty();
- windowFrames.setDisplayCutout(WmDisplayCutout.NO_CUTOUT);
+ mWindowFrames.calculateDockedDividerInsets(c.getDisplayCutout().getSafeInsets());
} else {
getDisplayContent().getBounds(mTmpRect);
- // Override right and/or bottom insets in case if the frame doesn't fit the screen in
- // non-fullscreen mode.
- boolean overrideRightInset = !windowsAreFloating && !inFullscreenContainer
- && mWindowFrames.mFrame.right > mTmpRect.right;
- boolean overrideBottomInset = !windowsAreFloating && !inFullscreenContainer
- && mWindowFrames.mFrame.bottom > mTmpRect.bottom;
- mContentInsets.set(mWindowFrames.mContentFrame.left - mWindowFrames.mFrame.left,
- mWindowFrames.mContentFrame.top - mWindowFrames.mFrame.top,
- overrideRightInset ? mTmpRect.right - mWindowFrames.mContentFrame.right
- : mWindowFrames.mFrame.right - mWindowFrames.mContentFrame.right,
- overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mContentFrame.bottom
- : mWindowFrames.mFrame.bottom - mWindowFrames.mContentFrame.bottom);
-
- mVisibleInsets.set(mWindowFrames.mVisibleFrame.left - mWindowFrames.mFrame.left,
- mWindowFrames.mVisibleFrame.top - mWindowFrames.mFrame.top,
- overrideRightInset ? mTmpRect.right - mWindowFrames.mVisibleFrame.right
- : mWindowFrames.mFrame.right - mWindowFrames.mVisibleFrame.right,
- overrideBottomInset ? mTmpRect.bottom - mWindowFrames.mVisibleFrame.bottom
- : mWindowFrames.mFrame.bottom - mWindowFrames.mVisibleFrame.bottom);
-
- mStableInsets.set(
- Math.max(mWindowFrames.mStableFrame.left - mWindowFrames.mFrame.left, 0),
- Math.max(mWindowFrames.mStableFrame.top - mWindowFrames.mFrame.top, 0),
- overrideRightInset ? Math.max(mTmpRect.right - mWindowFrames.mStableFrame.right,
- 0) : Math.max(
- mWindowFrames.mFrame.right - mWindowFrames.mStableFrame.right, 0),
- overrideBottomInset ? Math.max(
- mTmpRect.bottom - mWindowFrames.mStableFrame.bottom, 0) : Math.max(
- mWindowFrames.mFrame.bottom - mWindowFrames.mStableFrame.bottom, 0));
+ mWindowFrames.calculateInsets(windowsAreFloating, inFullscreenContainer, mTmpRect);
}
-
mWindowFrames.setDisplayCutout(
- windowFrames.mDisplayCutout.calculateRelativeTo(windowFrames.mFrame));
+ windowFrames.mDisplayCutout.calculateRelativeTo(mWindowFrames.mFrame));
// Offset the actual frame by the amount layout frame is off.
- mWindowFrames.mFrame.offset(-layoutXDiff, -layoutYDiff);
- mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
- mWindowFrames.mContentFrame.offset(-layoutXDiff, -layoutYDiff);
- mWindowFrames.mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
- mWindowFrames.mStableFrame.offset(-layoutXDiff, -layoutYDiff);
+ mWindowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
- mCompatFrame.set(mWindowFrames.mFrame);
+ mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
if (mEnforceSizeCompat) {
// If there is a size compatibility scale being applied to the
// window, we need to apply this to its insets so that they are
// reported to the app in its coordinate space.
- mOverscanInsets.scale(mInvGlobalScale);
- mContentInsets.scale(mInvGlobalScale);
- mVisibleInsets.scale(mInvGlobalScale);
- mStableInsets.scale(mInvGlobalScale);
- mOutsets.scale(mInvGlobalScale);
+ mWindowFrames.scaleInsets(mInvGlobalScale);
// Also the scaled frame that we report to the app needs to be
// adjusted to be in its coordinate space.
- mCompatFrame.scale(mInvGlobalScale);
+ mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
}
if (mIsWallpaper && (fw != mWindowFrames.mFrame.width()
@@ -1155,10 +1038,7 @@
+ mRequestedWidth + ", mRequestedheight="
+ mRequestedHeight + ") to" + " (pw=" + pw + ", ph=" + ph
+ "): frame=" + mWindowFrames.mFrame.toShortString()
- + " ci=" + mContentInsets.toShortString()
- + " vi=" + mVisibleInsets.toShortString()
- + " si=" + mStableInsets.toShortString()
- + " of=" + mOutsets.toShortString());
+ + " " + mWindowFrames.getInsetsInfo());
}
// TODO: Look into whether this override is still necessary.
@@ -1218,6 +1098,14 @@
return mWindowFrames.mDisplayCutout;
}
+ void getCompatFrame(Rect outFrame) {
+ outFrame.set(mWindowFrames.mCompatFrame);
+ }
+
+ void getCompatFrameSize(Rect outFrame) {
+ outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height());
+ }
+
@Override
public boolean getGivenInsetsPendingLw() {
return mGivenInsetsPending;
@@ -1269,15 +1157,7 @@
}
boolean setReportResizeHints() {
- mOverscanInsetsChanged |= !mLastOverscanInsets.equals(mOverscanInsets);
- mContentInsetsChanged |= !mLastContentInsets.equals(mContentInsets);
- mVisibleInsetsChanged |= !mLastVisibleInsets.equals(mVisibleInsets);
- mStableInsetsChanged |= !mLastStableInsets.equals(mStableInsets);
- mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
- mFrameSizeChanged |= mWindowFrames.didFrameSizeChange();
- mDisplayCutoutChanged |= mWindowFrames.didDisplayCutoutChange();
- return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
- || mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
+ return mWindowFrames.setReportResizeHints();
}
/**
@@ -1300,7 +1180,7 @@
return;
}
- setReportResizeHints();
+ boolean didFrameInsetsChange = setReportResizeHints();
boolean configChanged = isConfigChanged();
if (DEBUG_CONFIGURATION && configChanged) {
Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
@@ -1317,31 +1197,18 @@
// variables, because mFrameSizeChanged only tracks the width and height changing.
mWindowFrames.mLastFrame.set(mWindowFrames.mFrame);
- if (mContentInsetsChanged
- || mVisibleInsetsChanged
- || mStableInsetsChanged
+ if (didFrameInsetsChange
|| winAnimator.mSurfaceResized
- || mOutsetsChanged
- || mFrameSizeChanged
- || mDisplayCutoutChanged
|| configChanged
|| dragResizingChanged
|| mReportOrientationChanged) {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
Slog.v(TAG_WM, "Resize reasons for w=" + this + ": "
- + " contentInsetsChanged=" + mContentInsetsChanged
- + " " + mContentInsets.toShortString()
- + " visibleInsetsChanged=" + mVisibleInsetsChanged
- + " " + mVisibleInsets.toShortString()
- + " stableInsetsChanged=" + mStableInsetsChanged
- + " " + mStableInsets.toShortString()
- + " outsetsChanged=" + mOutsetsChanged
- + " " + mOutsets.toShortString()
+ + " " + mWindowFrames.getInsetsChangedInfo()
+ " surfaceResized=" + winAnimator.mSurfaceResized
+ " configChanged=" + configChanged
+ " dragResizingChanged=" + dragResizingChanged
- + " reportOrientationChanged=" + mReportOrientationChanged
- + " displayCutoutChanged=" + mDisplayCutoutChanged);
+ + " reportOrientationChanged=" + mReportOrientationChanged);
}
// If it's a dead window left on screen, and the configuration changed, there is nothing
@@ -3014,7 +2881,7 @@
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
try {
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
- + ": " + mCompatFrame);
+ + ": " + mWindowFrames.mCompatFrame);
final MergedConfiguration mergedConfiguration =
new MergedConfiguration(mService.mRoot.getConfiguration(),
getMergedOverrideConfiguration());
@@ -3025,11 +2892,11 @@
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
final Rect frame = mWindowFrames.mFrame;
- final Rect overscanInsets = mLastOverscanInsets;
- final Rect contentInsets = mLastContentInsets;
- final Rect visibleInsets = mLastVisibleInsets;
- final Rect stableInsets = mLastStableInsets;
- final Rect outsets = mLastOutsets;
+ final Rect overscanInsets = mWindowFrames.mLastOverscanInsets;
+ final Rect contentInsets = mWindowFrames.mLastContentInsets;
+ final Rect visibleInsets = mWindowFrames.mLastVisibleInsets;
+ final Rect stableInsets = mWindowFrames.mLastStableInsets;
+ final Rect outsets = mWindowFrames.mLastOutsets;
final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
final boolean reportOrientation = mReportOrientationChanged;
final int displayId = getDisplayId();
@@ -3060,13 +2927,7 @@
mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
}
- mOverscanInsetsChanged = false;
- mContentInsetsChanged = false;
- mVisibleInsetsChanged = false;
- mStableInsetsChanged = false;
- mOutsetsChanged = false;
- mFrameSizeChanged = false;
- mDisplayCutoutChanged = false;
+ mWindowFrames.resetInsetsChanged();
mWinAnimator.mSurfaceResized = false;
mReportOrientationChanged = false;
} catch (RemoteException e) {
@@ -3292,7 +3153,6 @@
mAttrs.writeToProto(proto, ATTRIBUTES);
mGivenContentInsets.writeToProto(proto, GIVEN_CONTENT_INSETS);
mWindowFrames.writeToProto(proto, WINDOW_FRAMES);
- mContentInsets.writeToProto(proto, CONTENT_INSETS);
mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
mWinAnimator.writeToProto(proto, ANIMATOR);
@@ -3306,10 +3166,6 @@
proto.write(SYSTEM_UI_VISIBILITY, mSystemUiVisibility);
proto.write(HAS_SURFACE, mHasSurface);
proto.write(IS_READY_FOR_DISPLAY, isReadyForDisplay());
- mOverscanInsets.writeToProto(proto, OVERSCAN_INSETS);
- mVisibleInsets.writeToProto(proto, VISIBLE_INSETS);
- mStableInsets.writeToProto(proto, STABLE_INSETS);
- mOutsets.writeToProto(proto, OUTSETS);
proto.write(REMOVE_ON_EXIT, mRemoveOnExit);
proto.write(DESTROYING, mDestroying);
proto.write(REMOVED, mRemoved);
@@ -3336,183 +3192,150 @@
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
final TaskStack stack = getStack();
- pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
- if (stack != null) {
- pw.print(" stackId="); pw.print(stack.mStackId);
- }
- pw.print(" mSession="); pw.print(mSession);
- pw.print(" mClient="); pw.println(mClient.asBinder());
- pw.print(prefix); pw.print("mOwnerUid="); pw.print(mOwnerUid);
- pw.print(" mShowToOwnerOnly="); pw.print(mShowToOwnerOnly);
- pw.print(" package="); pw.print(mAttrs.packageName);
- pw.print(" appop="); pw.println(AppOpsManager.opToName(mAppOp));
- pw.print(prefix); pw.print("mAttrs="); pw.println(mAttrs.toString(prefix));
- pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
- pw.print(" h="); pw.print(mRequestedHeight);
- pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
+ pw.print(prefix + "mDisplayId=" + getDisplayId());
+ if (stack != null) {
+ pw.print(" stackId=" + stack.mStackId);
+ }
+ pw.println(" mSession=" + mSession
+ + " mClient=" + mClient.asBinder());
+ pw.println(prefix + "mOwnerUid=" + mOwnerUid
+ + " mShowToOwnerOnly=" + mShowToOwnerOnly
+ + " package=" + mAttrs.packageName
+ + " appop=" + AppOpsManager.opToName(mAppOp));
+ pw.println(prefix + "mAttrs=" + mAttrs.toString(prefix));
+ pw.println(prefix + "Requested w=" + mRequestedWidth
+ + " h=" + mRequestedHeight
+ + " mLayoutSeq=" + mLayoutSeq);
if (mRequestedWidth != mLastRequestedWidth || mRequestedHeight != mLastRequestedHeight) {
- pw.print(prefix); pw.print("LastRequested w="); pw.print(mLastRequestedWidth);
- pw.print(" h="); pw.println(mLastRequestedHeight);
+ pw.println(prefix + "LastRequested w=" + mLastRequestedWidth
+ + " h=" + mLastRequestedHeight);
}
if (mIsChildWindow || mLayoutAttached) {
- pw.print(prefix); pw.print("mParentWindow="); pw.print(getParentWindow());
- pw.print(" mLayoutAttached="); pw.println(mLayoutAttached);
+ pw.println(prefix + "mParentWindow=" + getParentWindow()
+ + " mLayoutAttached=" + mLayoutAttached);
}
if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
- pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow);
- pw.print(" mIsWallpaper="); pw.print(mIsWallpaper);
- pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer);
- pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible);
+ pw.println(prefix + "mIsImWindow=" + mIsImWindow
+ + " mIsWallpaper=" + mIsWallpaper
+ + " mIsFloatingLayer=" + mIsFloatingLayer
+ + " mWallpaperVisible=" + mWallpaperVisible);
}
if (dumpAll) {
- pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
- pw.print(" mSubLayer="); pw.print(mSubLayer);
- pw.print(" mAnimLayer="); pw.print(mLayer); pw.print("+");
- pw.print("="); pw.print(mWinAnimator.mAnimLayer);
- pw.print(" mLastLayer="); pw.println(mWinAnimator.mLastLayer);
+ pw.println(prefix + "mBaseLayer=" + mBaseLayer
+ + " mSubLayer=" + mSubLayer
+ + " mAnimLayer=" + mLayer + "=" + mWinAnimator.mAnimLayer
+ + " mLastLayer=" + mWinAnimator.mLastLayer);
}
if (dumpAll) {
- pw.print(prefix); pw.print("mToken="); pw.println(mToken);
+ pw.println(prefix + "mToken=" + mToken);
if (mAppToken != null) {
- pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
- pw.print(prefix); pw.print(" isAnimatingWithSavedSurface()=");
- pw.print(" mAppDied=");pw.print(mAppDied);
- pw.print(prefix); pw.print("drawnStateEvaluated=");
- pw.print(getDrawnStateEvaluated());
- pw.print(prefix); pw.print("mightAffectAllDrawn=");
- pw.println(mightAffectAllDrawn());
+ pw.println(prefix + "mAppToken=" + mAppToken);
+ pw.print(prefix + "mAppDied=" + mAppDied);
+ pw.print(prefix + "drawnStateEvaluated=" + getDrawnStateEvaluated());
+ pw.println(prefix + "mightAffectAllDrawn=" + mightAffectAllDrawn());
}
- pw.print(prefix); pw.print("mViewVisibility=0x");
- pw.print(Integer.toHexString(mViewVisibility));
- pw.print(" mHaveFrame="); pw.print(mHaveFrame);
- pw.print(" mObscured="); pw.println(mObscured);
- pw.print(prefix); pw.print("mSeq="); pw.print(mSeq);
- pw.print(" mSystemUiVisibility=0x");
- pw.println(Integer.toHexString(mSystemUiVisibility));
+ pw.println(prefix + "mViewVisibility=0x" + Integer.toHexString(mViewVisibility)
+ + " mHaveFrame=" + mHaveFrame
+ + " mObscured=" + mObscured);
+ pw.println(prefix + "mSeq=" + mSeq
+ + " mSystemUiVisibility=0x" + Integer.toHexString(mSystemUiVisibility));
}
if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility
- || isParentWindowHidden()|| mPermanentlyHidden || mForceHideNonSystemOverlayWindow
+ || isParentWindowHidden() || mPermanentlyHidden || mForceHideNonSystemOverlayWindow
|| mHiddenWhileSuspended) {
- pw.print(prefix); pw.print("mPolicyVisibility=");
- pw.print(mPolicyVisibility);
- pw.print(" mPolicyVisibilityAfterAnim=");
- pw.print(mPolicyVisibilityAfterAnim);
- pw.print(" mAppOpVisibility=");
- pw.print(mAppOpVisibility);
- pw.print(" parentHidden="); pw.print(isParentWindowHidden());
- pw.print(" mPermanentlyHidden="); pw.print(mPermanentlyHidden);
- pw.print(" mHiddenWhileSuspended="); pw.print(mHiddenWhileSuspended);
- pw.print(" mForceHideNonSystemOverlayWindow="); pw.println(
- mForceHideNonSystemOverlayWindow);
+ pw.println(prefix + "mPolicyVisibility=" + mPolicyVisibility
+ + " mPolicyVisibilityAfterAnim=" + mPolicyVisibilityAfterAnim
+ + " mAppOpVisibility=" + mAppOpVisibility
+ + " parentHidden=" + isParentWindowHidden()
+ + " mPermanentlyHidden=" + mPermanentlyHidden
+ + " mHiddenWhileSuspended=" + mHiddenWhileSuspended
+ + " mForceHideNonSystemOverlayWindow=" + mForceHideNonSystemOverlayWindow);
}
if (!mRelayoutCalled || mLayoutNeeded) {
- pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled);
- pw.print(" mLayoutNeeded="); pw.println(mLayoutNeeded);
+ pw.println(prefix + "mRelayoutCalled=" + mRelayoutCalled
+ + " mLayoutNeeded=" + mLayoutNeeded);
}
if (dumpAll) {
- pw.print(prefix); pw.print("mGivenContentInsets=");
- mGivenContentInsets.printShortString(pw);
- pw.print(" mGivenVisibleInsets=");
- mGivenVisibleInsets.printShortString(pw);
- pw.println();
+ pw.println(prefix + "mGivenContentInsets=" + mGivenContentInsets.toShortString(sTmpSB)
+ + " mGivenVisibleInsets=" + mGivenVisibleInsets.toShortString(sTmpSB));
if (mTouchableInsets != 0 || mGivenInsetsPending) {
- pw.print(prefix); pw.print("mTouchableInsets="); pw.print(mTouchableInsets);
- pw.print(" mGivenInsetsPending="); pw.println(mGivenInsetsPending);
+ pw.println(prefix + "mTouchableInsets=" + mTouchableInsets
+ + " mGivenInsetsPending=" + mGivenInsetsPending);
Region region = new Region();
getTouchableRegion(region);
- pw.print(prefix); pw.print("touchable region="); pw.println(region);
+ pw.println(prefix + "touchable region=" + region);
}
- pw.print(prefix); pw.print("mFullConfiguration="); pw.println(getConfiguration());
- pw.print(prefix); pw.print("mLastReportedConfiguration=");
- pw.println(getLastReportedConfiguration());
+ pw.println(prefix + "mFullConfiguration=" + getConfiguration());
+ pw.println(prefix + "mLastReportedConfiguration=" + getLastReportedConfiguration());
}
- pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
- pw.print(" isReadyForDisplay()="); pw.print(isReadyForDisplay());
- pw.print(" mWindowRemovalAllowed="); pw.println(mWindowRemovalAllowed);
+ pw.println(prefix + "mHasSurface=" + mHasSurface
+ + " isReadyForDisplay()=" + isReadyForDisplay()
+ + " mWindowRemovalAllowed=" + mWindowRemovalAllowed);
if (mEnforceSizeCompat) {
- pw.print(prefix); pw.print("mCompatFrame="); mCompatFrame.printShortString(pw);
- pw.println();
+ pw.println(prefix + "mCompatFrame=" + mWindowFrames.mCompatFrame.toShortString(sTmpSB));
}
if (dumpAll) {
mWindowFrames.dump(pw, prefix);
- pw.print(prefix); pw.print("Cur insets: overscan=");
- mOverscanInsets.printShortString(pw);
- pw.print(" content="); mContentInsets.printShortString(pw);
- pw.print(" visible="); mVisibleInsets.printShortString(pw);
- pw.print(" stable="); mStableInsets.printShortString(pw);
- pw.print(" surface="); mAttrs.surfaceInsets.printShortString(pw);
- pw.print(" outsets="); mOutsets.printShortString(pw);
- pw.print(prefix); pw.print("Lst insets: overscan=");
- mLastOverscanInsets.printShortString(pw);
- pw.print(" content="); mLastContentInsets.printShortString(pw);
- pw.print(" visible="); mLastVisibleInsets.printShortString(pw);
- pw.print(" stable="); mLastStableInsets.printShortString(pw);
- pw.print(" physical="); mLastOutsets.printShortString(pw);
- pw.print(" outset="); mLastOutsets.printShortString(pw);
- pw.println();
+ pw.println(prefix + " surface=" + mAttrs.surfaceInsets.toShortString(sTmpSB));
}
super.dump(pw, prefix, dumpAll);
- pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
+ pw.println(prefix + mWinAnimator + ":");
mWinAnimator.dump(pw, prefix + " ", dumpAll);
if (mAnimatingExit || mRemoveOnExit || mDestroying || mRemoved) {
- pw.print(prefix); pw.print("mAnimatingExit="); pw.print(mAnimatingExit);
- pw.print(" mRemoveOnExit="); pw.print(mRemoveOnExit);
- pw.print(" mDestroying="); pw.print(mDestroying);
- pw.print(" mRemoved="); pw.println(mRemoved);
+ pw.println(prefix + "mAnimatingExit=" + mAnimatingExit
+ + " mRemoveOnExit=" + mRemoveOnExit
+ + " mDestroying=" + mDestroying
+ + " mRemoved=" + mRemoved);
}
if (getOrientationChanging() || mAppFreezing || mReportOrientationChanged) {
- pw.print(prefix); pw.print("mOrientationChanging=");
- pw.print(mOrientationChanging);
- pw.print(" configOrientationChanging=");
- pw.print(getLastReportedConfiguration().orientation
- != getConfiguration().orientation);
- pw.print(" mAppFreezing="); pw.print(mAppFreezing);
- pw.print(" mReportOrientationChanged="); pw.println(mReportOrientationChanged);
+ pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
+ + " configOrientationChanging="
+ + (getLastReportedConfiguration().orientation != getConfiguration().orientation)
+ + " mAppFreezing=" + mAppFreezing
+ + " mReportOrientationChanged=" + mReportOrientationChanged);
}
if (mLastFreezeDuration != 0) {
- pw.print(prefix); pw.print("mLastFreezeDuration=");
- TimeUtils.formatDuration(mLastFreezeDuration, pw); pw.println();
+ pw.print(prefix + "mLastFreezeDuration=");
+ TimeUtils.formatDuration(mLastFreezeDuration, pw);
+ pw.println();
}
- pw.print(prefix); pw.print("mForceSeamlesslyRotate="); pw.print(mForceSeamlesslyRotate);
- pw.print(" seamlesslyRotate: pending=");
+ pw.print(prefix + "mForceSeamlesslyRotate=" + mForceSeamlesslyRotate
+ + " seamlesslyRotate: pending=");
if (mPendingSeamlessRotate != null) {
mPendingSeamlessRotate.dump(pw);
} else {
pw.print("null");
}
- pw.print(" finishedFrameNumber="); pw.print(mFinishSeamlessRotateFrameNumber);
- pw.println();
+ pw.println(" finishedFrameNumber=" + mFinishSeamlessRotateFrameNumber);
if (mHScale != 1 || mVScale != 1) {
- pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
- pw.print(" mVScale="); pw.println(mVScale);
+ pw.println(prefix + "mHScale=" + mHScale
+ + " mVScale=" + mVScale);
}
if (mWallpaperX != -1 || mWallpaperY != -1) {
- pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX);
- pw.print(" mWallpaperY="); pw.println(mWallpaperY);
+ pw.println(prefix + "mWallpaperX=" + mWallpaperX
+ + " mWallpaperY=" + mWallpaperY);
}
if (mWallpaperXStep != -1 || mWallpaperYStep != -1) {
- pw.print(prefix); pw.print("mWallpaperXStep="); pw.print(mWallpaperXStep);
- pw.print(" mWallpaperYStep="); pw.println(mWallpaperYStep);
+ pw.println(prefix + "mWallpaperXStep=" + mWallpaperXStep
+ + " mWallpaperYStep=" + mWallpaperYStep);
}
if (mWallpaperDisplayOffsetX != Integer.MIN_VALUE
|| mWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
- pw.print(prefix); pw.print("mWallpaperDisplayOffsetX=");
- pw.print(mWallpaperDisplayOffsetX);
- pw.print(" mWallpaperDisplayOffsetY=");
- pw.println(mWallpaperDisplayOffsetY);
+ pw.println(prefix + "mWallpaperDisplayOffsetX=" + mWallpaperDisplayOffsetX
+ + " mWallpaperDisplayOffsetY=" + mWallpaperDisplayOffsetY);
}
if (mDrawLock != null) {
- pw.print(prefix); pw.println("mDrawLock=" + mDrawLock);
+ pw.println(prefix + "mDrawLock=" + mDrawLock);
}
if (isDragResizing()) {
- pw.print(prefix); pw.println("isDragResizing=" + isDragResizing());
+ pw.println(prefix + "isDragResizing=" + isDragResizing());
}
if (computeDragResizing()) {
- pw.print(prefix); pw.println("computeDragResizing=" + computeDragResizing());
+ pw.println(prefix + "computeDragResizing=" + computeDragResizing());
}
- pw.print(prefix); pw.println("isOnScreen=" + isOnScreen());
- pw.print(prefix); pw.println("isVisible=" + isVisible());
+ pw.println(prefix + "isOnScreen=" + isOnScreen());
+ pw.println(prefix + "isVisible=" + isVisible());
}
@Override
@@ -3553,7 +3376,7 @@
}
}
- void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
+ private void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
final int pw = containingFrame.width();
final int ph = containingFrame.height();
final Task task = getTask();
@@ -3631,10 +3454,10 @@
// We need to make sure we update the CompatFrame as it is used for
// cropping decisions, etc, on systems where we lack a decor layer.
- mCompatFrame.set(mWindowFrames.mFrame);
+ mWindowFrames.mCompatFrame.set(mWindowFrames.mFrame);
if (mEnforceSizeCompat) {
// See comparable block in computeFrameLw.
- mCompatFrame.scale(mInvGlobalScale);
+ mWindowFrames.mCompatFrame.scale(mInvGlobalScale);
}
}
@@ -4344,13 +4167,15 @@
// On a different display there is no system decor. Crop the window
// by the screen boundaries.
// TODO(multi-display)
- policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
- policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top,
- displayInfo.logicalWidth - mCompatFrame.left,
- displayInfo.logicalHeight - mCompatFrame.top);
+ 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, mCompatFrame.width(), mCompatFrame.height());
+ policyCrop.set(0, 0, mWindowFrames.mCompatFrame.width(),
+ mWindowFrames.mCompatFrame.height());
} else {
// Crop to the system decor specified by policy.
calculateSystemDecorRect(policyCrop);
@@ -4508,12 +4333,7 @@
* Updates the last inset values to the current ones.
*/
void updateLastInsetValues() {
- mLastOverscanInsets.set(mOverscanInsets);
- mLastContentInsets.set(mContentInsets);
- mLastVisibleInsets.set(mVisibleInsets);
- mLastStableInsets.set(mStableInsets);
- mLastOutsets.set(mOutsets);
- mWindowFrames.mLastDisplayCutout = mWindowFrames.mDisplayCutout;
+ mWindowFrames.updateLastInsetValues();
}
void startAnimation(Animation anim) {
@@ -4902,6 +4722,44 @@
}
}
+ /**
+ * Copy the inset values over so they can be sent back to the client when a relayout occurs.
+ */
+ void getInsetsForRelayout(Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
+ Rect outStableInsets, Rect outOutsets) {
+ outOverscanInsets.set(mWindowFrames.mOverscanInsets);
+ outContentInsets.set(mWindowFrames.mContentInsets);
+ outVisibleInsets.set(mWindowFrames.mVisibleInsets);
+ outStableInsets.set(mWindowFrames.mStableInsets);
+ outOutsets.set(mWindowFrames.mOutsets);
+
+ mLastRelayoutContentInsets.set(mWindowFrames.mContentInsets);
+ }
+
+ void getContentInsets(Rect outContentInsets) {
+ outContentInsets.set(mWindowFrames.mContentInsets);
+ }
+
+ Rect getContentInsets() {
+ return mWindowFrames.mContentInsets;
+ }
+
+ void getStableInsets(Rect outStableInsets) {
+ outStableInsets.set(mWindowFrames.mStableInsets);
+ }
+
+ Rect getStableInsets() {
+ return mWindowFrames.mStableInsets;
+ }
+
+ void resetLastContentInsets() {
+ mWindowFrames.resetLastContentInsets();
+ }
+
+ Rect getVisibleInsets() {
+ return mWindowFrames.mVisibleInsets;
+ }
+
private final class MoveAnimationSpec implements AnimationSpec {
private final long mDuration;
@@ -4934,9 +4792,9 @@
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix); pw.print("from="); pw.print(mFrom);
- pw.print(" to="); pw.print(mTo);
- pw.print(" duration="); pw.println(mDuration);
+ pw.println(prefix + "from=" + mFrom
+ + " to=" + mTo
+ + " duration=" + mDuration);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b158ae2..fec8039 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -24,6 +24,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_NONE;
+
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
@@ -46,7 +47,6 @@
import static com.android.server.wm.WindowStateAnimatorProto.SURFACE;
import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
-import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
import android.content.Context;
import android.graphics.Matrix;
@@ -476,8 +476,7 @@
flags |= SurfaceControl.SECURE;
}
- mTmpSize.set(0, 0, 0, 0);
- calculateSurfaceBounds(w, attrs);
+ calculateSurfaceBounds(w, attrs, mTmpSize);
final int width = mTmpSize.width();
final int height = mTmpSize.height();
@@ -556,44 +555,38 @@
return mSurfaceController;
}
- private void calculateSurfaceBounds(WindowState w, LayoutParams attrs) {
+ 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.
- mTmpSize.right = mTmpSize.left + w.mRequestedWidth;
- mTmpSize.bottom = mTmpSize.top + w.mRequestedHeight;
+ 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()) {
- if (w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM) {
- mTmpSize.left = 0;
- mTmpSize.top = 0;
- }
final DisplayInfo displayInfo = w.getDisplayInfo();
- mTmpSize.right = mTmpSize.left + displayInfo.logicalWidth;
- mTmpSize.bottom = mTmpSize.top + displayInfo.logicalHeight;
+ outSize.right = displayInfo.logicalWidth;
+ outSize.bottom = displayInfo.logicalHeight;
} else {
- mTmpSize.right = mTmpSize.left + w.mCompatFrame.width();
- mTmpSize.bottom = mTmpSize.top + w.mCompatFrame.height();
+ w.getCompatFrameSize(outSize);
}
}
// Something is wrong and SurfaceFlinger will not like this, try to revert to sane 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 (mTmpSize.width() < 1) {
- mTmpSize.right = mTmpSize.left + 1;
+ if (outSize.width() < 1) {
+ outSize.right = 1;
}
- if (mTmpSize.height() < 1) {
- mTmpSize.bottom = mTmpSize.top + 1;
+ if (outSize.height() < 1) {
+ outSize.bottom = 1;
}
// Adjust for surface insets.
- mTmpSize.left -= attrs.surfaceInsets.left;
- mTmpSize.top -= attrs.surfaceInsets.top;
- mTmpSize.right += attrs.surfaceInsets.right;
- mTmpSize.bottom += attrs.surfaceInsets.bottom;
+ outSize.inset(-attrs.surfaceInsets.left, -attrs.surfaceInsets.top,
+ -attrs.surfaceInsets.right, -attrs.surfaceInsets.bottom);
}
boolean hasSurface() {
@@ -870,8 +863,7 @@
final LayoutParams attrs = mWin.getAttrs();
final Task task = w.getTask();
- mTmpSize.set(0, 0, 0, 0);
- calculateSurfaceBounds(w, attrs);
+ calculateSurfaceBounds(w, attrs, mTmpSize);
mExtraHScale = (float) 1.0;
mExtraVScale = (float) 1.0;
@@ -1070,7 +1062,8 @@
// comes in at the new size (normally position and crop are unfrozen).
// setGeometryAppliesWithResizeInTransaction accomplishes this for us.
if (wasForceScaled && !mForceScaleUntilResize) {
- mSurfaceController.setGeometryAppliesWithResizeInTransaction(true);
+ mSurfaceController.deferTransactionUntil(mSurfaceController.getHandle(),
+ mWin.getFrameNumber());
mSurfaceController.forceScaleableInTransaction(false);
}
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
index 62bab07..d29d3fc 100644
--- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -25,6 +25,7 @@
#include <usbhost/usbhost.h>
#define MAX_DESCRIPTORS_LENGTH 4096
+static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
// com.android.server.usb.descriptors
extern "C" {
@@ -85,8 +86,9 @@
jbyte* byteBuffer = NULL;
size_t numUSC2Bytes = 0;
int retVal =
- usb_device_get_string_ucs2(device, stringId, 0 /*timeout*/,
- (void**)&byteBuffer, &numUSC2Bytes);
+ usb_device_get_string_ucs2(device, stringId,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS,
+ (void**)&byteBuffer, &numUSC2Bytes);
jstring j_str = NULL;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 52f2d67..42ade38 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -210,7 +210,7 @@
const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
- void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray);
+ void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj);
void setInputDispatchMode(bool enabled, bool frozen);
void setSystemUiVisibility(int32_t visibility);
@@ -230,11 +230,11 @@
virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
- virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier);
+ virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env,
jfloatArray matrixArr);
virtual TouchAffineTransformation getTouchAffineTransformation(
- const String8& inputDeviceDescriptor, int32_t surfaceRotation);
+ const std::string& inputDeviceDescriptor, int32_t surfaceRotation);
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -480,7 +480,7 @@
for (jsize i = 0; i < length; i++) {
jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i));
const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
- outConfig->excludedDeviceNames.add(String8(deviceNameChars));
+ outConfig->excludedDeviceNames.push_back(deviceNameChars);
env->ReleaseStringUTFChars(item, deviceNameChars);
env->DeleteLocalRef(item);
}
@@ -606,7 +606,7 @@
JNIEnv* env = jniEnv();
sp<KeyCharacterMap> result;
- ScopedLocalRef<jstring> descriptor(env, env->NewStringUTF(identifier.descriptor.string()));
+ ScopedLocalRef<jstring> descriptor(env, env->NewStringUTF(identifier.descriptor.c_str()));
ScopedLocalRef<jobject> identifierObj(env, env->NewObject(gInputDeviceIdentifierInfo.clazz,
gInputDeviceIdentifierInfo.constructor, descriptor.get(),
identifier.vendor, identifier.product));
@@ -620,24 +620,24 @@
ScopedUtfChars filenameChars(env, filenameObj.get());
ScopedUtfChars contentsChars(env, contentsObj.get());
- KeyCharacterMap::loadContents(String8(filenameChars.c_str()),
- String8(contentsChars.c_str()), KeyCharacterMap::FORMAT_OVERLAY, &result);
+ KeyCharacterMap::loadContents(filenameChars.c_str(),
+ contentsChars.c_str(), KeyCharacterMap::FORMAT_OVERLAY, &result);
}
checkAndClearExceptionFromCallback(env, "getKeyboardLayoutOverlay");
return result;
}
-String8 NativeInputManager::getDeviceAlias(const InputDeviceIdentifier& identifier) {
+std::string NativeInputManager::getDeviceAlias(const InputDeviceIdentifier& identifier) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
- ScopedLocalRef<jstring> uniqueIdObj(env, env->NewStringUTF(identifier.uniqueId.string()));
+ ScopedLocalRef<jstring> uniqueIdObj(env, env->NewStringUTF(identifier.uniqueId.c_str()));
ScopedLocalRef<jstring> aliasObj(env, jstring(env->CallObjectMethod(mServiceObj,
gServiceClassInfo.getDeviceAlias, uniqueIdObj.get())));
- String8 result;
+ std::string result;
if (aliasObj.get()) {
ScopedUtfChars aliasChars(env, aliasObj.get());
- result.setTo(aliasChars.c_str());
+ result = aliasChars.c_str();
}
checkAndClearExceptionFromCallback(env, "getDeviceAlias");
return result;
@@ -736,7 +736,8 @@
}
}
-void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
+void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray,
+ int32_t displayId) {
Vector<sp<InputWindowHandle> > windowHandles;
if (windowHandleObjArray) {
@@ -756,7 +757,7 @@
}
}
- mInputManager->getDispatcher()->setInputWindows(windowHandles);
+ mInputManager->getDispatcher()->setInputWindows(windowHandles, displayId);
// Do this after the dispatcher has updated the window handle state.
bool newPointerGesturesEnabled = true;
@@ -932,10 +933,10 @@
}
TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
- const String8& inputDeviceDescriptor, int32_t surfaceRotation) {
+ const std::string& inputDeviceDescriptor, int32_t surfaceRotation) {
JNIEnv* env = jniEnv();
- ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string()));
+ ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.c_str()));
jobject cal = env->CallObjectMethod(mServiceObj,
gServiceClassInfo.getTouchCalibrationForInputDevice, descriptorObj.get(),
@@ -1281,7 +1282,7 @@
v.deviceWidth = deviceWidth;
v.deviceHeight = deviceHeight;
if (uniqueId != nullptr) {
- v.uniqueId.setTo(ScopedUtfChars(env, uniqueId).c_str());
+ v.uniqueId = ScopedUtfChars(env, uniqueId).c_str();
}
im->setDisplayViewport(viewportType, v);
@@ -1446,10 +1447,10 @@
}
static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobjectArray windowHandleObjArray) {
+ jlong ptr, jobjectArray windowHandleObjArray, jint displayId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->setInputWindows(env, windowHandleObjArray);
+ im->setInputWindows(env, windowHandleObjArray, displayId);
}
static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
@@ -1678,7 +1679,7 @@
(void*) nativeInjectInputEvent },
{ "nativeToggleCapsLock", "(JI)V",
(void*) nativeToggleCapsLock },
- { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V",
+ { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
(void*) nativeSetInputWindows },
{ "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
(void*) nativeSetFocusedApplication },
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c131858..a1132d7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -377,6 +377,7 @@
private static final Set<String> GLOBAL_SETTINGS_WHITELIST;
private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
+ private static final Set<Integer> DA_DISALLOWED_POLICIES;
static {
SECURE_SETTINGS_WHITELIST = new ArraySet<>();
SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -408,6 +409,12 @@
SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS);
SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_OFF_TIMEOUT);
+
+ DA_DISALLOWED_POLICIES = new ArraySet<>();
+ DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA);
+ DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
+ DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD);
+ DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
}
/**
@@ -2203,6 +2210,7 @@
* @return the user password metrics, or {@code null} if none have been associated with
* the user yet (for example, if the device has booted but not been unlocked).
*/
+ @GuardedBy("getLockObject()")
PasswordMetrics getUserPasswordMetricsLocked(int userHandle) {
return mUserPasswordMetrics.get(userHandle);
}
@@ -2607,6 +2615,9 @@
final int userId = UserHandle.getUserId(callingUid);
final DevicePolicyData policy = getUserData(userId);
ActiveAdmin admin = policy.mAdminMap.get(who);
+ final boolean isDeviceOwner = isDeviceOwner(admin.info.getComponent(), userId);
+ final boolean isProfileOwner = isProfileOwner(admin.info.getComponent(), userId);
+
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
throw new SecurityException("Admin " + admin.info.getComponent()
+ " does not own the device");
@@ -2615,6 +2626,11 @@
throw new SecurityException("Admin " + admin.info.getComponent()
+ " does not own the profile");
}
+ if (DA_DISALLOWED_POLICIES.contains(reqPolicy) && !isDeviceOwner && !isProfileOwner) {
+ throw new SecurityException("Admin " + admin.info.getComponent()
+ + " is not a device owner or profile owner, so may not use policy: "
+ + admin.info.getTagForPolicy(reqPolicy));
+ }
throw new SecurityException("Admin " + admin.info.getComponent()
+ " did not specify uses-policy for: "
+ admin.info.getTagForPolicy(reqPolicy));
@@ -2694,7 +2710,10 @@
// DO always has the PO power.
return ownsDevice || ownsProfile;
} else {
- return admin.info.usesPolicy(reqPolicy);
+ boolean allowedToUsePolicy = ownsDevice || ownsProfile
+ || !DA_DISALLOWED_POLICIES.contains(reqPolicy)
+ || getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.Q;
+ return allowedToUsePolicy && admin.info.usesPolicy(reqPolicy);
}
}
@@ -3972,6 +3991,7 @@
* be the correct one upon boot.
* This should be called whenever the password or the admin policies have changed.
*/
+ @GuardedBy("getLockObject()")
private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
final int credentialOwner = getCredentialOwner(userHandle, parent);
DevicePolicyData policy = getUserData(credentialOwner);
@@ -12313,6 +12333,7 @@
}
/** Pauses security and network logging if there are unaffiliated users on the device */
+ @GuardedBy("getLockObject()")
private void maybePauseDeviceWideLoggingLocked() {
if (!areAllUsersAffiliatedWithDeviceLocked()) {
Slog.i(LOG_TAG, "There are unaffiliated users, security and network logging will be "
@@ -12325,6 +12346,7 @@
}
/** Resumes security and network logging (if they are enabled) if all users are affiliated */
+ @GuardedBy("getLockObject()")
private void maybeResumeDeviceWideLoggingLocked() {
if (areAllUsersAffiliatedWithDeviceLocked()) {
final long ident = mInjector.binderClearCallingIdentity();
@@ -12340,6 +12362,7 @@
}
/** Deletes any security and network logs that might have been collected so far */
+ @GuardedBy("getLockObject()")
private void discardDeviceWideLogsLocked() {
mSecurityLogMonitor.discardLogs();
if (mNetworkLogger != null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ecc13b2..0b6a33f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -743,10 +743,20 @@
traceEnd();
}
+ // Tracks and caches the device state.
+ traceBeginAndSlog("StartCachedDeviceStateService");
+ mSystemServiceManager.startService(CachedDeviceStateService.class);
+ traceEnd();
+
// Tracks cpu time spent in binder calls
traceBeginAndSlog("StartBinderCallsStatsService");
mSystemServiceManager.startService(BinderCallsStatsService.LifeCycle.class);
traceEnd();
+
+ // Tracks time spent in handling messages in handlers.
+ traceBeginAndSlog("StartLooperStatsService");
+ mSystemServiceManager.startService(LooperStatsService.Lifecycle.class);
+ traceEnd();
}
/**
@@ -873,7 +883,8 @@
}
traceBeginAndSlog("StartAlarmManagerService");
- mSystemServiceManager.startService(AlarmManagerService.class);
+ mSystemServiceManager.startService(new AlarmManagerService(context));
+
traceEnd();
traceBeginAndSlog("InitWatchdog");
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 3cdef1e..ccc092d 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -1385,6 +1385,20 @@
private boolean startIpReachabilityMonitor() {
try {
+ // TODO: Fetch these parameters from settings, and install a
+ // settings observer to watch for update and re-program these
+ // parameters (Q: is this level of dynamic updatability really
+ // necessary or does reading from settings at startup suffice?).
+ final int NUM_SOLICITS = 5;
+ final int INTER_SOLICIT_INTERVAL_MS = 750;
+ setNeighborParameters(mDependencies.getNetd(), mInterfaceName,
+ NUM_SOLICITS, INTER_SOLICIT_INTERVAL_MS);
+ } catch (Exception e) {
+ mLog.e("Failed to adjust neighbor parameters", e);
+ // Carry on using the system defaults (currently: 3, 1000);
+ }
+
+ try {
mIpReachabilityMonitor = new IpReachabilityMonitor(
mContext,
mInterfaceParams,
@@ -1863,6 +1877,20 @@
}
}
+ private static void setNeighborParameters(
+ INetd netd, String ifName, int num_solicits, int inter_solicit_interval_ms)
+ throws RemoteException, IllegalArgumentException {
+ Preconditions.checkNotNull(netd);
+ Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
+ Preconditions.checkArgument(num_solicits > 0);
+ Preconditions.checkArgument(inter_solicit_interval_ms > 0);
+
+ for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
+ netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms", Integer.toString(inter_solicit_interval_ms));
+ netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit", Integer.toString(num_solicits));
+ }
+ }
+
// TODO: extract out into CollectionUtils.
static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
for (T t : coll) {
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 8fbc01e..9d686ef 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -119,13 +119,23 @@
private volatile UnicastResponder mUnicastResponder;
public static class RaParams {
+ // Tethered traffic will have the hop limit properly decremented.
+ // Consequently, set the hoplimit greater by one than the upstream
+ // unicast hop limit.
+ //
+ // TODO: Dynamically pass down the IPV6_UNICAST_HOPS value from the
+ // upstream interface for more correct behaviour.
+ static final byte DEFAULT_HOPLIMIT = 65;
+
public boolean hasDefaultRoute;
+ public byte hopLimit;
public int mtu;
public HashSet<IpPrefix> prefixes;
public HashSet<Inet6Address> dnses;
public RaParams() {
hasDefaultRoute = false;
+ hopLimit = DEFAULT_HOPLIMIT;
mtu = IPV6_MIN_MTU;
prefixes = new HashSet<IpPrefix>();
dnses = new HashSet<Inet6Address>();
@@ -133,6 +143,7 @@
public RaParams(RaParams other) {
hasDefaultRoute = other.hasDefaultRoute;
+ hopLimit = other.hopLimit;
mtu = other.mtu;
prefixes = (HashSet) other.prefixes.clone();
dnses = (HashSet) other.dnses.clone();
@@ -273,10 +284,12 @@
final ByteBuffer ra = ByteBuffer.wrap(mRA);
ra.order(ByteOrder.BIG_ENDIAN);
+ final boolean haveRaParams = (mRaParams != null);
boolean shouldSendRA = false;
try {
- putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
+ putHeader(ra, haveRaParams && mRaParams.hasDefaultRoute,
+ haveRaParams ? mRaParams.hopLimit : RaParams.DEFAULT_HOPLIMIT);
putSlla(ra, mInterface.macAddr.toByteArray());
mRaLength = ra.position();
@@ -287,7 +300,7 @@
//
// putExpandedFlagsOption(ra);
- if (mRaParams != null) {
+ if (haveRaParams) {
putMtu(ra, mRaParams.mtu);
mRaLength = ra.position();
@@ -348,7 +361,7 @@
private static byte asByte(int value) { return (byte) value; }
private static short asShort(int value) { return (short) value; }
- private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute) {
+ private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute, byte hopLimit) {
/**
Router Advertisement Message Format
@@ -366,11 +379,10 @@
| Options ...
+-+-+-+-+-+-+-+-+-+-+-+-
*/
- final byte DEFAULT_HOPLIMIT = 64;
ra.put(ICMPV6_ND_ROUTER_ADVERT)
.put(asByte(0))
.putShort(asShort(0))
- .put(DEFAULT_HOPLIMIT)
+ .put(hopLimit)
// RFC 4191 "high" preference, iff. advertising a default route.
.put(hasDefaultRoute ? asByte(0x08) : asByte(0))
.putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 3d7fdbdd..2691701 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -63,7 +63,9 @@
$(call all-Iaidl-files-under, ../../core/java/android/app/backup) \
../../core/java/android/content/pm/PackageInfo.java \
../../core/java/android/app/IBackupAgent.aidl \
- ../../core/java/android/util/KeyValueSettingObserver.java
+ ../../core/java/android/util/KeyValueSettingObserver.java \
+ ../../core/java/android/content/pm/PackageParser.java \
+ ../../core/java/android/content/pm/SigningInfo.java
LOCAL_AIDL_INCLUDES := \
$(call all-Iaidl-files-under, $(INTERNAL_BACKUP)) \
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
new file mode 100644
index 0000000..112e1e3
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -0,0 +1,495 @@
+package com.android.server.backup.fullbackup;
+
+import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
+import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_VERSION;
+import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_FILENAME;
+import static com.android.server.backup.BackupManagerService.BACKUP_METADATA_VERSION;
+import static com.android.server.backup.BackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.Nullable;
+import android.app.Application;
+import android.app.backup.BackupDataInput;
+import android.app.backup.FullBackupDataOutput;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderClasses;
+import com.android.server.testing.SystemLoaderPackages;
+import com.android.server.testing.shadows.ShadowBackupDataInput;
+import com.android.server.testing.shadows.ShadowBackupDataOutput;
+import com.android.server.testing.shadows.ShadowFullBackup;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.attribute.FileTime;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
+import org.robolectric.shadows.ShadowEnvironment;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(
+ manifest = Config.NONE,
+ sdk = 26,
+ shadows = {
+ ShadowBackupDataInput.class,
+ ShadowBackupDataOutput.class,
+ ShadowEnvironment.class,
+ ShadowFullBackup.class,
+ })
+@SystemLoaderPackages({"com.android.server.backup", "android.app.backup"})
+@SystemLoaderClasses({PackageInfo.class, SigningInfo.class})
+public class AppMetadataBackupWriterTest {
+ private static final String TEST_PACKAGE = "com.test.package";
+ private static final String TEST_PACKAGE_INSTALLER = "com.test.package.installer";
+ private static final Long TEST_PACKAGE_VERSION_CODE = 100L;
+
+ private ShadowApplicationPackageManager mShadowPackageManager;
+ private File mFilesDir;
+ private File mBackupDataOutputFile;
+ private AppMetadataBackupWriter mBackupWriter;
+
+ @Before
+ public void setUp() throws Exception {
+ Application application = RuntimeEnvironment.application;
+
+ PackageManager packageManager = application.getPackageManager();
+ mShadowPackageManager = (ShadowApplicationPackageManager) shadowOf(packageManager);
+
+ mFilesDir = RuntimeEnvironment.application.getFilesDir();
+ mBackupDataOutputFile = new File(mFilesDir, "output");
+ mBackupDataOutputFile.createNewFile();
+ ParcelFileDescriptor pfd =
+ ParcelFileDescriptor.open(
+ mBackupDataOutputFile, ParcelFileDescriptor.MODE_READ_WRITE);
+ FullBackupDataOutput output =
+ new FullBackupDataOutput(pfd, /* quota */ -1, /* transportFlags */ 0);
+ mBackupWriter = new AppMetadataBackupWriter(output, packageManager);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mBackupDataOutputFile.delete();
+ }
+
+ /**
+ * The manifest format is:
+ *
+ * <pre>
+ * BACKUP_MANIFEST_VERSION
+ * package name
+ * package version code
+ * platform version code
+ * installer package name (can be empty)
+ * boolean (1 if archive includes .apk, otherwise 0)
+ * # of signatures N
+ * N* (signature byte array in ascii format per Signature.toCharsString())
+ * </pre>
+ */
+ @Test
+ public void testBackupManifest_withoutApkOrSignatures_writesCorrectData() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
+
+ mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false);
+
+ byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
+ String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n");
+ assertThat(manifest.length).isEqualTo(7);
+ assertThat(manifest[0]).isEqualTo(Integer.toString(BACKUP_MANIFEST_VERSION));
+ assertThat(manifest[1]).isEqualTo(TEST_PACKAGE);
+ assertThat(manifest[2]).isEqualTo(Long.toString(TEST_PACKAGE_VERSION_CODE));
+ assertThat(manifest[3]).isEqualTo(Integer.toString(Build.VERSION.SDK_INT));
+ assertThat(manifest[4]).isEqualTo(TEST_PACKAGE_INSTALLER);
+ assertThat(manifest[5]).isEqualTo("0"); // withApk
+ assertThat(manifest[6]).isEqualTo("0"); // signatures
+ manifestFile.delete();
+ }
+
+ /**
+ * The manifest format is:
+ *
+ * <pre>
+ * BACKUP_MANIFEST_VERSION
+ * package name
+ * package version code
+ * platform version code
+ * installer package name (can be empty)
+ * boolean (1 if archive includes .apk, otherwise 0)
+ * # of signatures N
+ * N* (signature byte array in ascii format per Signature.toCharsString())
+ * </pre>
+ */
+ @Test
+ public void testBackupManifest_withApk_writesApk() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
+
+ mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ true);
+
+ byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
+ String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n");
+ assertThat(manifest.length).isEqualTo(7);
+ assertThat(manifest[5]).isEqualTo("1"); // withApk
+ manifestFile.delete();
+ }
+
+ /**
+ * The manifest format is:
+ *
+ * <pre>
+ * BACKUP_MANIFEST_VERSION
+ * package name
+ * package version code
+ * platform version code
+ * installer package name (can be empty)
+ * boolean (1 if archive includes .apk, otherwise 0)
+ * # of signatures N
+ * N* (signature byte array in ascii format per Signature.toCharsString())
+ * </pre>
+ */
+ @Test
+ public void testBackupManifest_withSignatures_writesCorrectSignatures() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ packageInfo.signingInfo =
+ new SigningInfo(
+ new SigningDetails(
+ new Signature[] {new Signature("1234"), new Signature("5678")},
+ SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null,
+ null));
+ File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
+
+ mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false);
+
+ byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
+ String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n");
+ assertThat(manifest.length).isEqualTo(9);
+ assertThat(manifest[6]).isEqualTo("2"); // # of signatures
+ assertThat(manifest[7]).isEqualTo("1234"); // first signature
+ assertThat(manifest[8]).isEqualTo("5678"); // second signature
+ manifestFile.delete();
+ }
+
+ /**
+ * The manifest format is:
+ *
+ * <pre>
+ * BACKUP_MANIFEST_VERSION
+ * package name
+ * package version code
+ * platform version code
+ * installer package name (can be empty)
+ * boolean (1 if archive includes .apk, otherwise 0)
+ * # of signatures N
+ * N* (signature byte array in ascii format per Signature.toCharsString())
+ * </pre>
+ */
+ @Config(sdk = VERSION_CODES.O)
+ @Test
+ public void testBackupManifest_whenApiO_writesCorrectApi() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
+
+ mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false);
+
+ byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
+ String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n");
+ assertThat(manifest.length).isEqualTo(7);
+ assertThat(manifest[3]).isEqualTo(Integer.toString(VERSION_CODES.O)); // platform version
+ manifestFile.delete();
+ }
+
+ /**
+ * The manifest format is:
+ *
+ * <pre>
+ * BACKUP_MANIFEST_VERSION
+ * package name
+ * package version code
+ * platform version code
+ * installer package name (can be empty)
+ * boolean (1 if archive includes .apk, otherwise 0)
+ * # of signatures N
+ * N* (signature byte array in ascii format per Signature.toCharsString())
+ * </pre>
+ */
+ @Test
+ public void testBackupManifest_withoutInstallerPackage_writesEmptyInstaller() throws Exception {
+ PackageInfo packageInfo = createPackageInfo(TEST_PACKAGE, null, TEST_PACKAGE_VERSION_CODE);
+ File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
+
+ mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false);
+
+ byte[] manifestBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
+ String[] manifest = new String(manifestBytes, StandardCharsets.UTF_8).split("\n");
+ assertThat(manifest.length).isEqualTo(7);
+ assertThat(manifest[4]).isEqualTo(""); // installer package name
+ manifestFile.delete();
+ }
+
+ @Test
+ public void testBackupManifest_whenRunPreviouslyWithSameData_producesSameBytesOnSecondRun()
+ throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
+ mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false);
+ byte[] firstRunBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ true);
+ // Simulate modifying the manifest file to ensure that file metadata does not change the
+ // backup bytes produced.
+ modifyFileMetadata(manifestFile);
+
+ mBackupWriter.backupManifest(packageInfo, manifestFile, mFilesDir, /* withApk */ false);
+
+ byte[] secondRunBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ true);
+ assertThat(firstRunBytes).isEqualTo(secondRunBytes);
+ manifestFile.delete();
+ }
+
+ /**
+ * The widget data format with metadata is:
+ *
+ * <pre>
+ * BACKUP_METADATA_VERSION
+ * package name
+ * 4 : Integer token identifying the widget data blob.
+ * 4 : Integer size of the widget data.
+ * N : Raw bytes of the widget data.
+ * </pre>
+ */
+ @Test
+ public void testBackupWidget_writesCorrectData() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File metadataFile = createFile(BACKUP_METADATA_FILENAME);
+ byte[] widgetBytes = "widget".getBytes();
+
+ mBackupWriter.backupWidget(packageInfo, metadataFile, mFilesDir, widgetBytes);
+
+ byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
+ String[] widgetData = new String(writtenBytes, StandardCharsets.UTF_8).split("\n");
+ assertThat(widgetData.length).isEqualTo(3);
+ // Metadata header
+ assertThat(widgetData[0]).isEqualTo(Integer.toString(BACKUP_METADATA_VERSION));
+ assertThat(widgetData[1]).isEqualTo(packageInfo.packageName);
+ // Widget data
+ ByteArrayOutputStream expectedBytes = new ByteArrayOutputStream();
+ DataOutputStream stream = new DataOutputStream(expectedBytes);
+ stream.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
+ stream.writeInt(widgetBytes.length);
+ stream.write(widgetBytes);
+ stream.flush();
+ assertThat(widgetData[2]).isEqualTo(expectedBytes.toString());
+ metadataFile.delete();
+ }
+
+ @Test
+ public void testBackupWidget_withNullWidgetData_throwsNullPointerException() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File metadataFile = createFile(BACKUP_METADATA_FILENAME);
+
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ mBackupWriter.backupWidget(
+ packageInfo, metadataFile, mFilesDir, /* widgetData */ null));
+
+ metadataFile.delete();
+ }
+
+ @Test
+ public void testBackupWidget_withEmptyWidgetData_throwsIllegalArgumentException()
+ throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File metadataFile = createFile(BACKUP_METADATA_FILENAME);
+
+ expectThrows(
+ IllegalArgumentException.class,
+ () ->
+ mBackupWriter.backupWidget(
+ packageInfo, metadataFile, mFilesDir, new byte[0]));
+
+ metadataFile.delete();
+ }
+
+ @Test
+ public void testBackupWidget_whenRunPreviouslyWithSameData_producesSameBytesOnSecondRun()
+ throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File metadataFile = createFile(BACKUP_METADATA_FILENAME);
+ byte[] widgetBytes = "widget".getBytes();
+ mBackupWriter.backupWidget(packageInfo, metadataFile, mFilesDir, widgetBytes);
+ byte[] firstRunBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ true);
+ // Simulate modifying the metadata file to ensure that file metadata does not change the
+ // backup bytes produced.
+ modifyFileMetadata(metadataFile);
+
+ mBackupWriter.backupWidget(packageInfo, metadataFile, mFilesDir, widgetBytes);
+
+ byte[] secondRunBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ true);
+ assertThat(firstRunBytes).isEqualTo(secondRunBytes);
+ metadataFile.delete();
+ }
+
+ @Test
+ public void testBackupApk_writesCorrectBytesToOutput() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ byte[] apkBytes = "apk".getBytes();
+ File apkFile = createApkFileAndWrite(apkBytes);
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.sourceDir = apkFile.getPath();
+
+ mBackupWriter.backupApk(packageInfo);
+
+ byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
+ assertThat(writtenBytes).isEqualTo(apkBytes);
+ apkFile.delete();
+ }
+
+ @Test
+ public void testBackupObb_withObbData_writesCorrectBytesToOutput() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File obbDir = createObbDirForPackage(packageInfo.packageName);
+ byte[] obbBytes = "obb".getBytes();
+ File obbFile = createObbFileAndWrite(obbDir, obbBytes);
+
+ mBackupWriter.backupObb(packageInfo);
+
+ byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
+ assertThat(writtenBytes).isEqualTo(obbBytes);
+ obbFile.delete();
+ }
+
+ @Test
+ public void testBackupObb_withNoObbData_doesNotWriteBytesToOutput() throws Exception {
+ PackageInfo packageInfo =
+ createPackageInfo(TEST_PACKAGE, TEST_PACKAGE_INSTALLER, TEST_PACKAGE_VERSION_CODE);
+ File obbDir = createObbDirForPackage(packageInfo.packageName);
+ // No obb file created.
+
+ mBackupWriter.backupObb(packageInfo);
+
+ assertThat(mBackupDataOutputFile.length()).isEqualTo(0);
+ }
+
+ /**
+ * Creates a test package and registers it with the package manager. Also sets the installer
+ * package name if not {@code null}.
+ */
+ private PackageInfo createPackageInfo(
+ String packageName, @Nullable String installerPackageName, long versionCode) {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
+ packageInfo.setLongVersionCode(versionCode);
+ mShadowPackageManager.addPackage(packageInfo);
+ if (installerPackageName != null) {
+ mShadowPackageManager.setInstallerPackageName(packageName, installerPackageName);
+ }
+ return packageInfo;
+ }
+
+ /**
+ * Reads backup data written to the {@code file} by {@link ShadowBackupDataOutput}. Uses {@link
+ * ShadowBackupDataInput} to parse the data. Follows the format used by {@link
+ * ShadowFullBackup#backupToTar(String, String, String, String, String, FullBackupDataOutput)}.
+ *
+ * @param includeTarHeader If {@code true}, returns the TAR header and data bytes combined.
+ * Otherwise, only returns the data bytes.
+ */
+ private byte[] getWrittenBytes(File file, boolean includeTarHeader) throws IOException {
+ BackupDataInput input = new BackupDataInput(new FileInputStream(file).getFD());
+ input.readNextHeader();
+ int dataSize = input.getDataSize();
+
+ byte[] bytes;
+ if (includeTarHeader) {
+ bytes = new byte[dataSize + 512];
+ input.readEntityData(bytes, 0, dataSize + 512);
+ } else {
+ input.readEntityData(new byte[512], 0, 512); // skip TAR header
+ bytes = new byte[dataSize];
+ input.readEntityData(bytes, 0, dataSize);
+ }
+
+ return bytes;
+ }
+
+ private File createFile(String fileName) throws IOException {
+ File file = new File(mFilesDir, fileName);
+ file.createNewFile();
+ return file;
+ }
+
+ /**
+ * Sets the last modified time of the {@code file} to the current time to edit the file's
+ * metadata.
+ */
+ private void modifyFileMetadata(File file) throws IOException {
+ Files.setLastModifiedTime(file.toPath(), FileTime.fromMillis(System.currentTimeMillis()));
+ }
+
+ private File createApkFileAndWrite(byte[] data) throws IOException {
+ File apkFile = new File(mFilesDir, "apk");
+ apkFile.createNewFile();
+ Files.write(apkFile.toPath(), data);
+ return apkFile;
+ }
+
+ /** Creates an .obb file in the input directory. */
+ private File createObbFileAndWrite(File obbDir, byte[] data) throws IOException {
+ File obbFile = new File(obbDir, "obb");
+ obbFile.createNewFile();
+ Files.write(obbFile.toPath(), data);
+ return obbFile;
+ }
+
+ /**
+ * Creates a package specific obb data directory since the backup method checks for obb data
+ * there. See {@link Environment#buildExternalStorageAppObbDirs(String)}.
+ */
+ private File createObbDirForPackage(String packageName) {
+ ShadowEnvironment.addExternalDir("test");
+ Environment.UserEnvironment userEnv =
+ new Environment.UserEnvironment(UserHandle.USER_SYSTEM);
+ File obbDir =
+ new File(
+ userEnv.getExternalDirs()[0],
+ Environment.DIR_ANDROID + "/obb/" + packageName);
+ obbDir.mkdirs();
+ return obbDir;
+ }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 9fae43a..b4bc9d1 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -63,6 +63,7 @@
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadow.api.Shadow.extract;
import static org.testng.Assert.fail;
+import static org.testng.Assert.expectThrows;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.Collections.emptyList;
@@ -818,7 +819,7 @@
}
@Test
- public void testRunTask_whenAgentOnBackupThrows_updatesAndCleansUpFiles() throws Exception {
+ public void testRunTask_whenAgentOnBackupThrows_updatesFilesAndCleansUp() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
AgentMock agentMock = setUpAgent(PACKAGE_1);
remoteAgentOnBackupThrows(
@@ -833,8 +834,7 @@
assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
.isEqualTo("oldState".getBytes());
- assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+ assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
@Test
@@ -913,7 +913,7 @@
}
@Test
- public void testRunTask_whenAgentUsesProhibitedKey_updatesAndCleansUpFiles() throws Exception {
+ public void testRunTask_whenAgentUsesProhibitedKey_updatesFilesAndCleansUp() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
AgentMock agentMock = setUpAgent(PACKAGE_1);
agentOnBackupDo(
@@ -930,8 +930,7 @@
assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
.isEqualTo("oldState".getBytes());
- assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+ assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
@Test
@@ -1093,7 +1092,7 @@
}
@Test
- public void testRunTask_whenAgentDoesNotWriteData_updatesAndCleansUpFiles() throws Exception {
+ public void testRunTask_whenAgentDoesNotWriteData_updatesFilesAndCleansUp() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
AgentMock agentMock = setUpAgent(PACKAGE_1);
agentOnBackupDo(
@@ -1106,8 +1105,7 @@
runTask(task);
assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1))).isEqualTo(new byte[0]);
- assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+ assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
@Test
@@ -1158,7 +1156,7 @@
}
@Test
- public void testRunTask_whenFinishBackupSucceeds_updatesAndCleansUpFiles() throws Exception {
+ public void testRunTask_whenFinishBackupSucceeds_updatesFilesAndCleansUp() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
AgentMock agentMock = setUpAgent(PACKAGE_1);
@@ -1174,8 +1172,7 @@
assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
.isEqualTo("newState".getBytes());
- assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+ assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
@Test
@@ -1235,7 +1232,7 @@
}
@Test
- public void testRunTask_whenTransportRejectsPackage_updatesAndCleansUpFiles() throws Exception {
+ public void testRunTask_whenTransportRejectsPackage_updatesFilesAndCleansUp() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
when(transportMock.transport.performBackup(
argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
@@ -1248,8 +1245,7 @@
assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
.isEqualTo("oldState".getBytes());
- assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+ assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
@Test
@@ -1349,7 +1345,9 @@
runTask(task);
- verify(agentMock.agent).onQuotaExceeded(anyLong(), eq(1234L));
+ InOrder inOrder = inOrder(agentMock.agent, mBackupManagerService);
+ inOrder.verify(agentMock.agent).onQuotaExceeded(anyLong(), eq(1234L));
+ inOrder.verify(mBackupManagerService).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
}
@Test
@@ -1396,8 +1394,7 @@
runTask(task);
- assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+ assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
@Test
@@ -1412,8 +1409,7 @@
runTask(task);
assertThat(isFileNonEmpty(getStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+ assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
@Test
@@ -1670,7 +1666,7 @@
}
@Test
- public void testRunTask_whenTransportReturnsError_updatesAndCleansUpFiles() throws Exception {
+ public void testRunTask_whenTransportReturnsError_updatesFilesAndCleansUp() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
when(transportMock.transport.performBackup(
argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
@@ -1683,8 +1679,7 @@
assertThat(Files.readAllBytes(getStateFile(mTransport, PACKAGE_1)))
.isEqualTo("oldState".getBytes());
- assertThat(Files.exists(getTemporaryStateFile(mTransport, PACKAGE_1))).isFalse();
- assertThat(Files.exists(getStagingFile(PACKAGE_1))).isFalse();
+ assertCleansUpFilesAndAgent(mTransport, PACKAGE_1);
}
@Test
@@ -1732,20 +1727,54 @@
}
@Test
- public void testRunTask_whenReadingBackupDataThrows() throws Exception {
+ public void testRunTask_whenReadingBackupDataThrows_reportsCorrectly() throws Exception {
TransportMock transportMock = setUpInitializedTransport(mTransport);
setUpAgentWithData(PACKAGE_1);
- AgentMock agentMock = setUpAgentWithData(PACKAGE_2);
- KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
// We don't validate PM's data, so it will only throw in PACKAGE_1
ShadowBackupDataInput.throwInNextHeaderRead();
runTask(task);
verify(mReporter).onReadAgentDataError(eq(PACKAGE_1.packageName), any());
+ }
+
+ @Test
+ public void testRunTask_whenReadingBackupDataThrows_doesNotCallTransport() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+ ShadowBackupDataInput.throwInNextHeaderRead();
+
+ runTask(task);
+
verify(transportMock.transport, never())
.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+ }
+
+ @Test
+ public void testRunTask_whenReadingBackupDataThrows_doesNotCallSecondAgent() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ AgentMock agentMock = setUpAgentWithData(PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+ ShadowBackupDataInput.throwInNextHeaderRead();
+
+ runTask(task);
+
verify(agentMock.agent, never()).onBackup(any(), any(), any());
+ }
+
+ @Test
+ public void testRunTask_whenReadingBackupDataThrows_cleansUpAndRevertsTask() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentsWithData(PACKAGE_1, PACKAGE_2);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+ ShadowBackupDataInput.throwInNextHeaderRead();
+
+ runTask(task);
+
+ assertCleansUpFiles(mTransport, PACKAGE_2);
assertTaskReverted(transportMock, PACKAGE_1, PACKAGE_2);
}
@@ -1941,6 +1970,29 @@
task.markCancel();
}
+ @Test
+ public void testHandleCancel_callsMarkCancelAndWaitCancel() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ KeyValueBackupTask task = spy(createKeyValueBackupTask(transportMock, PACKAGE_1));
+ doNothing().when(task).waitCancel();
+
+ task.handleCancel(true);
+
+ InOrder inOrder = inOrder(task);
+ inOrder.verify(task).markCancel();
+ inOrder.verify(task).waitCancel();
+ }
+
+ @Test
+ public void testHandleCancel_whenCancelAllFalse_throws() throws Exception {
+ TransportMock transportMock = setUpInitializedTransport(mTransport);
+ setUpAgentWithData(PACKAGE_1);
+ KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+ expectThrows(IllegalArgumentException.class, () -> task.handleCancel(false));
+ }
+
private void runTask(KeyValueBackupTask task) {
// Pretend we are not on the main-thread to prevent RemoteCall from complaining
mShadowMainLooper.setCurrentThread(false);
@@ -2361,6 +2413,16 @@
assertThat(data1).isEqualTo(value);
}
+ private void assertCleansUpFilesAndAgent(TransportData transport, PackageData packageData) {
+ assertCleansUpFiles(transport, packageData);
+ verify(mBackupManagerService).unbindAgent(argThat(applicationInfo(packageData)));
+ }
+
+ private void assertCleansUpFiles(TransportData transport, PackageData packageData) {
+ assertThat(Files.exists(getTemporaryStateFile(transport, packageData))).isFalse();
+ assertThat(Files.exists(getStagingFile(packageData))).isFalse();
+ }
+
/**
* Put conditions that should *always* be true after task execution.
*
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
index ca04008..5812c3c 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
@@ -55,6 +55,11 @@
return mTransportFlags;
}
+ public ObjectOutputStream getOutputStream() {
+ ensureOutput();
+ return mOutput;
+ }
+
@Implementation
public int writeEntityHeader(String key, int dataSize) throws IOException {
ensureOutput();
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowFullBackup.java b/services/robotests/src/com/android/server/testing/shadows/ShadowFullBackup.java
new file mode 100644
index 0000000..3c913e3
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowFullBackup.java
@@ -0,0 +1,70 @@
+package com.android.server.testing.shadows;
+
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackup;
+import android.app.backup.FullBackupDataOutput;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Shadow for {@link FullBackup}. Used to emulate the native method {@link
+ * FullBackup#backupToTar(String, String, String, String, String, FullBackupDataOutput)}. Relies on
+ * the shadow {@link ShadowBackupDataOutput}, which must be included in tests that use this shadow.
+ */
+@Implements(FullBackup.class)
+public class ShadowFullBackup {
+ /**
+ * Reads data from the specified file at {@code path} and writes it to the {@code output}. Does
+ * not match the native implementation, and only partially simulates TAR format. Used solely for
+ * passing backup data for testing purposes.
+ *
+ * <p>Note: Only handles the {@code path} denoting a file and not a directory like the real
+ * implementation.
+ */
+ @Implementation
+ public static int backupToTar(
+ String packageName,
+ String domain,
+ String linkdomain,
+ String rootpath,
+ String path,
+ FullBackupDataOutput output) {
+ BackupDataOutput backupDataOutput = output.getData();
+ try {
+ Path file = Paths.get(path);
+ byte[] data = Files.readAllBytes(file);
+ backupDataOutput.writeEntityHeader("key", data.length);
+
+ // Partially simulate TAR header (not all fields included). We use a 512 byte block for
+ // the header to follow the TAR convention and to have a consistent size block to help
+ // with separating the header from the data.
+ ByteBuffer tarBlock = ByteBuffer.wrap(new byte[512]);
+ String tarPath = "apps/" + packageName + (domain == null ? "" : "/" + domain) + path;
+ tarBlock.put(tarPath.getBytes()); // file path
+ tarBlock.putInt(0x1ff); // file mode
+ tarBlock.putLong(Files.size(file)); // file size
+ tarBlock.putLong(Files.getLastModifiedTime(file).toMillis()); // last modified time
+ tarBlock.putInt(0); // file type
+
+ // Write TAR header directly to the BackupDataOutput's output stream.
+ ShadowBackupDataOutput shadowBackupDataOutput = Shadow.extract(backupDataOutput);
+ ObjectOutputStream outputStream = shadowBackupDataOutput.getOutputStream();
+ outputStream.write(tarBlock.array());
+ outputStream.flush();
+
+ backupDataOutput.writeEntityData(data, data.length);
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ return 0;
+ }
+}
diff --git a/services/tests/mockingservicestests/Android.mk b/services/tests/mockingservicestests/Android.mk
new file mode 100644
index 0000000..8c02833
--- /dev/null
+++ b/services/tests/mockingservicestests/Android.mk
@@ -0,0 +1,44 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ frameworks-base-testutils \
+ services.core \
+ androidx-test \
+ mockito-target-extended-minus-junit4 \
+ platform-test-annotations \
+ ShortcutManagerTestUtils \
+ truth-prebuilt \
+
+LOCAL_JAVA_LIBRARIES := android.test.mock android.test.base android.test.runner
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+ libdexmakerjvmtiagent \
+ libstaticjvmtiagent \
+
+LOCAL_CERTIFICATE := platform
+LOCAL_PACKAGE_NAME := FrameworksMockingServicesTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
new file mode 100644
index 0000000..c9aa631
--- /dev/null
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.mockingservicestests">
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+
+ <application android:testOnly="true"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.mockingservicestests"
+ android:label="Frameworks Mocking Services Tests" />
+</manifest>
diff --git a/services/tests/mockingservicestests/AndroidTest.xml b/services/tests/mockingservicestests/AndroidTest.xml
new file mode 100644
index 0000000..7782d57
--- /dev/null
+++ b/services/tests/mockingservicestests/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks Services Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="FrameworksMockingServicesTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksMockingServicesTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.mockingservicestests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
new file mode 100644
index 0000000..de3d285
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server;
+
+import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static org.junit.Assert.assertEquals;
+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.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.app.IUidObserver;
+import android.app.PendingIntent;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+
+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 org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+
+import javax.annotation.concurrent.GuardedBy;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AlarmManagerServiceTest {
+ private static final String TAG = AlarmManagerServiceTest.class.getSimpleName();
+ private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package";
+ private static final int SYSTEM_UI_UID = 123456789;
+ private static final int TEST_CALLING_UID = 12345;
+ private static final long DEFAULT_TIMEOUT = 5_000;
+
+ private AlarmManagerService mService;
+ @Mock
+ private IActivityManager mIActivityManager;
+ @Mock
+ private UsageStatsManagerInternal mUsageStatsManagerInternal;
+ @Mock
+ private AppStateTracker mAppStateTracker;
+ @Mock
+ private AlarmManagerService.ClockReceiver mClockReceiver;
+ @Mock
+ private PowerManager.WakeLock mWakeLock;
+
+ private MockitoSession mMockingSession;
+ private Injector mInjector;
+ private volatile long mNowElapsedTest;
+ @GuardedBy("mTestTimer")
+ private TestTimer mTestTimer = new TestTimer();
+
+ static class TestTimer {
+ private long mElapsed;
+ boolean mExpired;
+
+ synchronized long getElapsed() {
+ return mElapsed;
+ }
+
+ synchronized void set(long millisElapsed) {
+ mElapsed = millisElapsed;
+ }
+
+ synchronized long expire() {
+ mExpired = true;
+ notify();
+ return mElapsed;
+ }
+ }
+
+ public class Injector extends AlarmManagerService.Injector {
+ Injector(Context context) {
+ super(context);
+ }
+
+ @Override
+ void init() {
+ // Do nothing.
+ }
+
+ @Override
+ int waitForAlarm() {
+ synchronized (mTestTimer) {
+ if (!mTestTimer.mExpired) {
+ try {
+ mTestTimer.wait();
+ } catch (InterruptedException ie) {
+ Log.e(TAG, "Wait interrupted!", ie);
+ return 0;
+ }
+ }
+ mTestTimer.mExpired = false;
+ }
+ return AlarmManagerService.IS_WAKEUP_MASK; // Doesn't matter, just evaluate.
+ }
+
+ @Override
+ void setKernelTimezone(int minutesWest) {
+ // Do nothing.
+ }
+
+ @Override
+ void setAlarm(int type, long millis) {
+ mTestTimer.set(millis);
+ }
+
+ @Override
+ void setKernelTime(long millis) {
+ }
+
+ @Override
+ int getSystemUiUid() {
+ return SYSTEM_UI_UID;
+ }
+
+ @Override
+ boolean isAlarmDriverPresent() {
+ // Pretend the driver is present, so code does not fall back to handler
+ return true;
+ }
+
+ @Override
+ long getElapsedRealtime() {
+ return mNowElapsedTest;
+ }
+
+ @Override
+ AlarmManagerService.ClockReceiver getClockReceiver(AlarmManagerService service) {
+ return mClockReceiver;
+ }
+
+ @Override
+ PowerManager.WakeLock getAlarmWakeLock() {
+ return mWakeLock;
+ }
+ }
+
+ @Before
+ public final void setUp() throws Exception {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(ActivityManager.class, Answers.CALLS_REAL_METHODS)
+ .mockStatic(LocalServices.class)
+ .mockStatic(Looper.class, Answers.CALLS_REAL_METHODS)
+ .startMocking();
+ doReturn(mIActivityManager).when(ActivityManager::getService);
+ doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
+ doReturn(null)
+ .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class));
+ doReturn(mUsageStatsManagerInternal).when(
+ () -> LocalServices.getService(UsageStatsManagerInternal.class));
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
+ eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong()))
+ .thenReturn(STANDBY_BUCKET_ACTIVE);
+ doReturn(Looper.getMainLooper()).when(Looper::myLooper);
+
+ final Context context = InstrumentationRegistry.getTargetContext();
+ mInjector = spy(new Injector(context));
+ mService = new AlarmManagerService(context, mInjector);
+ spyOn(mService);
+ doNothing().when(mService).publishBinderService(any(), any());
+ mService.onStart();
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ mService.mConstants.MIN_FUTURITY = 0;
+
+ assertEquals(mService.mSystemUiUid, SYSTEM_UI_UID);
+ assertEquals(mService.mClockReceiver, mClockReceiver);
+ assertEquals(mService.mWakeLock, mWakeLock);
+ verify(mIActivityManager).registerUidObserver(any(IUidObserver.class), anyInt(), anyInt(),
+ isNull());
+ }
+
+ private void setTestAlarm(int type, long triggerTime, PendingIntent operation) {
+ mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, 0,
+ operation, null, "test", AlarmManager.FLAG_STANDALONE, null, null,
+ TEST_CALLING_UID, TEST_CALLING_PACKAGE);
+ }
+
+ private PendingIntent getNewMockPendingIntent() {
+ final PendingIntent mockPi = mock(PendingIntent.class, Answers.RETURNS_DEEP_STUBS);
+ when(mockPi.getCreatorUid()).thenReturn(TEST_CALLING_UID);
+ when(mockPi.getCreatorPackage()).thenReturn(TEST_CALLING_PACKAGE);
+ return mockPi;
+ }
+
+ @Test
+ public void testSingleAlarmSet() {
+ final long triggerTime = mNowElapsedTest + 5000;
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
+ verify(mInjector).setAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime);
+ assertEquals(triggerTime, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void testSingleAlarmExpiration() throws Exception {
+ final long triggerTime = mNowElapsedTest + 5000;
+ final PendingIntent alarmPi = getNewMockPendingIntent();
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi);
+
+ mNowElapsedTest = mTestTimer.expire();
+
+ final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor =
+ ArgumentCaptor.forClass(PendingIntent.OnFinished.class);
+ verify(alarmPi, timeout(DEFAULT_TIMEOUT)).send(any(Context.class), eq(0),
+ any(Intent.class), onFinishedCaptor.capture(), any(Handler.class), isNull(), any());
+ verify(mWakeLock, timeout(DEFAULT_TIMEOUT)).acquire();
+ onFinishedCaptor.getValue().onSendFinished(alarmPi, null, 0, null, null);
+ verify(mWakeLock, timeout(DEFAULT_TIMEOUT)).release();
+ }
+
+ @Test
+ public void testMinFuturity() {
+ mService.mConstants.MIN_FUTURITY = 10;
+ final long triggerTime = mNowElapsedTest + 1;
+ final long expectedTriggerTime = mNowElapsedTest + mService.mConstants.MIN_FUTURITY;
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, getNewMockPendingIntent());
+ verify(mInjector).setAlarm(ELAPSED_REALTIME_WAKEUP, expectedTriggerTime);
+ }
+
+ @Test
+ public void testEarliestAlarmSet() {
+ final PendingIntent pi6 = getNewMockPendingIntent();
+ final PendingIntent pi8 = getNewMockPendingIntent();
+ final PendingIntent pi9 = getNewMockPendingIntent();
+
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 8, pi8);
+ assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed());
+
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 9, pi9);
+ assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed());
+
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, pi6);
+ assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed());
+
+ mService.removeLocked(pi6, null);
+ assertEquals(mNowElapsedTest + 8, mTestTimer.getElapsed());
+
+ mService.removeLocked(pi8, null);
+ assertEquals(mNowElapsedTest + 9, mTestTimer.getElapsed());
+ }
+
+ @Test
+ public void testStandbyBucketDelay_workingSet() throws Exception {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
+ assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
+
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(STANDBY_BUCKET_WORKING_SET);
+ mNowElapsedTest = mTestTimer.expire();
+ verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce())
+ .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
+ eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
+ final long expectedNextTrigger = mNowElapsedTest
+ + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_WORKING_SET);
+ assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: "
+ + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT,
+ () -> (mTestTimer.getElapsed() == expectedNextTrigger)));
+ }
+
+ @Test
+ public void testStandbyBucketDelay_frequent() {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
+ assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
+
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(STANDBY_BUCKET_FREQUENT);
+ mNowElapsedTest = mTestTimer.expire();
+ verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce())
+ .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
+ eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
+ final long expectedNextTrigger = mNowElapsedTest
+ + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_FREQUENT);
+ assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: "
+ + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT,
+ () -> (mTestTimer.getElapsed() == expectedNextTrigger)));
+ }
+
+ @Test
+ public void testStandbyBucketDelay_rare() {
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, getNewMockPendingIntent());
+ setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, getNewMockPendingIntent());
+ assertEquals(mNowElapsedTest + 5, mTestTimer.getElapsed());
+
+ when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE), anyInt(),
+ anyLong())).thenReturn(STANDBY_BUCKET_RARE);
+ mNowElapsedTest = mTestTimer.expire();
+ verify(mUsageStatsManagerInternal, timeout(DEFAULT_TIMEOUT).atLeastOnce())
+ .getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
+ eq(UserHandle.getUserId(TEST_CALLING_UID)), anyLong());
+ final long expectedNextTrigger = mNowElapsedTest
+ + mService.getMinDelayForBucketLocked(STANDBY_BUCKET_RARE);
+ assertTrue("Incorrect next alarm trigger. Expected " + expectedNextTrigger + " found: "
+ + mTestTimer.getElapsed(), pollingCheck(DEFAULT_TIMEOUT,
+ () -> (mTestTimer.getElapsed() == expectedNextTrigger)));
+ }
+
+ @After
+ public void tearDown() {
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
+ }
+
+ private boolean pollingCheck(long timeout, Condition condition) {
+ final long deadline = SystemClock.uptimeMillis() + timeout;
+ boolean interrupted = false;
+ while (!condition.check() && SystemClock.uptimeMillis() < deadline) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ie) {
+ interrupted = true;
+ }
+ }
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ return condition.check();
+ }
+
+ @FunctionalInterface
+ interface Condition {
+ boolean check();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
index 910aad7f..c8e6782 100644
--- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
@@ -64,9 +64,6 @@
import android.util.ArraySet;
import android.util.Pair;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
import com.android.server.AppStateTracker.Listener;
@@ -88,11 +85,14 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
/**
* Tests for {@link AppStateTracker}
*
* Run with:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/mockingservicestests/src/com/android/server/AppStateTrackerTest.java
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -683,10 +683,10 @@
//--------------------------------------------------
List<OpEntry> entries = new ArrayList<>();
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
@@ -694,7 +694,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
@@ -702,7 +702,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null));
@@ -710,10 +710,10 @@
//--------------------------------------------------
entries = new ArrayList<>();
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
- entries.add(new AppOpsManager.OpEntry(
+ entries.add(new OpEntry(
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
@@ -754,7 +754,7 @@
final AppStateTrackerTestable instance = newInstance();
callStart(instance);
- AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
+ Listener l = mock(Listener.class);
instance.addListener(l);
// Power save on.
@@ -797,7 +797,7 @@
final AppStateTrackerTestable instance = newInstance();
callStart(instance);
- AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
+ Listener l = mock(Listener.class);
instance.addListener(l);
// -------------------------------------------------------------------------
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 43f319e..80307ee 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -9,7 +9,9 @@
LOCAL_MODULE_TAGS := tests
# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, utils) \
LOCAL_STATIC_JAVA_LIBRARIES := \
frameworks-base-testutils \
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 348e201..863e487 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -143,7 +143,8 @@
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
<activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
- <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity" />
+ <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
+ android:showWhenLocked="true"/>
<activity android:name="com.android.server.pm.ShortcutTestActivity"
android:enabled="true" android:exported="true" />
diff --git a/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java
index 1f63d61..d248b89 100644
--- a/services/tests/servicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BackgroundRestrictedAlarmsTest.java
@@ -35,14 +35,13 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class AlarmManagerServiceTest {
+public class BackgroundRestrictedAlarmsTest {
private SparseArray<ArrayList<Alarm>> addPendingAlarm(
SparseArray<ArrayList<Alarm>> all, int uid, String name, boolean removeIt) {
ArrayList<Alarm> uidAlarms = all.get(uid);
if (uidAlarms == null) {
all.put(uid, uidAlarms = new ArrayList<>());
}
- // Details don't matter.
uidAlarms.add(new Alarm(
removeIt ? RTC : RTC_WAKEUP,
0, 0, 0, 0, 0, null, null, null, null, 0, null, uid, name));
diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
new file mode 100644
index 0000000..81107cf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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;
+
+
+import static org.mockito.Mockito.when;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
+import android.os.IPowerManager;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
+import android.os.RemoteException;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.os.CachedDeviceState;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link CachedDeviceStateService}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CachedDeviceStateServiceTest {
+ @Mock private BatteryManagerInternal mBatteryManager;
+ @Mock private IPowerManager mPowerManager;
+ private BroadcastInterceptingContext mContext;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ Context context = InstrumentationRegistry.getContext();
+ PowerManager powerManager = new PowerManager(context, mPowerManager, null);
+ mContext = new BroadcastInterceptingContext(context) {
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.POWER_SERVICE:
+ return powerManager;
+ default:
+ return super.getSystemService(name);
+ }
+ }
+ };
+
+ LocalServices.addService(BatteryManagerInternal.class, mBatteryManager);
+
+ when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+ when(mPowerManager.isInteractive()).thenReturn(true);
+ }
+
+ @After
+ public void tearDown() {
+ // Added by the CachedDeviceStateService.onStart().
+ LocalServices.removeServiceForTest(CachedDeviceState.Readonly.class);
+
+ // Added in @Before.
+ LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+ }
+
+ @Test
+ public void correctlyReportsScreenInteractive() throws RemoteException {
+ CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+ when(mPowerManager.isInteractive()).thenReturn(true); // Screen on.
+
+ service.onStart();
+ CachedDeviceState.Readonly deviceState =
+ LocalServices.getService(CachedDeviceState.Readonly.class);
+
+ // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+ assertThat(deviceState.isScreenInteractive()).isFalse();
+
+ service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ assertThat(deviceState.isScreenInteractive()).isTrue();
+
+ mContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
+ assertThat(deviceState.isScreenInteractive()).isFalse();
+
+ mContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
+ assertThat(deviceState.isScreenInteractive()).isTrue();
+ }
+
+ @Test
+ public void correctlyReportsCharging() {
+ CachedDeviceStateService service = new CachedDeviceStateService(mContext);
+ when(mBatteryManager.getPlugType()).thenReturn(OsProtoEnums.BATTERY_PLUGGED_NONE);
+
+ service.onStart();
+ CachedDeviceState.Readonly deviceState =
+ LocalServices.getService(CachedDeviceState.Readonly.class);
+
+ // State can be initialized correctly only after PHASE_SYSTEM_SERVICES_READY.
+ assertThat(deviceState.isCharging()).isTrue();
+
+ service.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ assertThat(deviceState.isCharging()).isFalse();
+
+ Intent intentPluggedIn = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intentPluggedIn.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+ mContext.sendBroadcast(intentPluggedIn);
+ assertThat(deviceState.isCharging()).isTrue();
+
+ Intent intentUnplugged = new Intent(Intent.ACTION_BATTERY_CHANGED);
+ intentUnplugged.putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
+ mContext.sendBroadcast(intentUnplugged);
+ assertThat(deviceState.isCharging()).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
index 360ccbf..a3decb9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureControllerTest.java
@@ -20,6 +20,8 @@
import android.accessibilityservice.FingerprintGestureController.FingerprintGestureCallback;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.os.Looper;
+import android.support.test.filters.FlakyTest;
+
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -95,6 +97,7 @@
}
@Test
+ @FlakyTest
public void testDetectionActiveCallback_withHandler_shouldPostRunnableToHandler() {
MessageCapturingHandler messageCapturingHandler = new MessageCapturingHandler((message) -> {
message.getCallback().run();
@@ -142,6 +145,7 @@
}
@Test
+ @FlakyTest
public void testGestureCallback_withHandler_shouldPostRunnableToHandler() {
MessageCapturingHandler messageCapturingHandler = new MessageCapturingHandler((message) -> {
message.getCallback().run();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
index f9d264b..d6d21c6 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationControllerTest.java
@@ -43,6 +43,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.support.test.filters.FlakyTest;
import android.view.MagnificationSpec;
import androidx.test.runner.AndroidJUnit4;
@@ -66,6 +67,7 @@
import java.util.Locale;
@RunWith(AndroidJUnit4.class)
+@FlakyTest
public class MagnificationControllerTest {
static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 100, 200);
static final PointF INITIAL_MAGNIFICATION_BOUNDS_CENTER = new PointF(
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 20df2ae..1aa80c8 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -33,9 +33,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
@@ -46,7 +44,6 @@
import android.app.WaitResult;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.util.SparseIntArray;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -54,7 +51,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.invocation.InvocationOnMock;
import java.util.ArrayList;
@@ -246,22 +242,6 @@
null /* target */, null /* targetOptions */);
}
- @Test
- public void testTopRunningActivityLockedWithNonExistentDisplay() throws Exception {
- // Create display that ActivityManagerService does not know about
- final int unknownDisplayId = 100;
-
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
- displayIds.put(0, 0);
- displayIds.put(1, unknownDisplayId);
- return null;
- }).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
-
- // Supervisor should skip over the non-existent display.
- assertEquals(null, mSupervisor.topRunningActivityLocked());
- }
-
/**
* Verifies that removal of activity with task and stack is done correctly.
*/
@@ -339,12 +319,6 @@
final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
.setStack(stack).build();
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
- displayIds.put(0, display.mDisplayId);
- return null;
- }).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
-
// Make sure the top running activity is not affected when keyguard is not locked
assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked(
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 59b0890..1023bc1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -189,7 +189,7 @@
assertEquals(task.getTopActivity(false /* includeOverlays */), r);
assertEquals(task.getTopActivity(true /* includeOverlays */), taskOverlay);
- assertNotNull(result.r);
+ assertNotNull(result.mRecord);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 9c0b525..aef5537 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -35,7 +35,6 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
@@ -45,7 +44,6 @@
import com.android.server.wm.DisplayWindowController;
import org.junit.Rule;
-import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import android.app.IApplicationThread;
@@ -63,26 +61,22 @@
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
-import android.util.SparseIntArray;
import androidx.test.InstrumentationRegistry;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.AttributeCache;
import com.android.server.wm.AppWindowContainerController;
-import com.android.server.wm.DisplayWindowController;
import com.android.server.wm.PinnedStackWindowController;
+import com.android.server.wm.RootWindowContainerController;
import com.android.server.wm.StackWindowController;
import com.android.server.wm.TaskWindowContainerController;
import com.android.server.wm.WindowManagerService;
import com.android.server.wm.WindowTestUtils;
-import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
import java.util.List;
@@ -500,13 +494,14 @@
(DisplayManager) mService.mContext.getSystemService(Context.DISPLAY_SERVICE);
mWindowManager = prepareMockWindowManager();
mKeyguardController = mock(KeyguardController.class);
+ setWindowContainerController(mock(RootWindowContainerController.class));
}
@Override
public void initialize() {
super.initialize();
mDisplay = spy(new TestActivityDisplay(this, DEFAULT_DISPLAY));
- attachDisplay(mDisplay);
+ addChild(mDisplay, ActivityDisplay.POSITION_TOP);
}
@Override
@@ -576,12 +571,6 @@
return null;
}).when(service).inSurfaceTransaction(any());
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
- displayIds.put(0, 0);
- return null;
- }).when(service).getDisplaysInFocusOrder(any());
-
return service;
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index ba82487..5195214 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -872,8 +872,8 @@
super.initialize();
mDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
mOtherDisplay = new TestActivityDisplay(this, DEFAULT_DISPLAY);
- attachDisplay(mOtherDisplay);
- attachDisplay(mDisplay);
+ addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+ addChild(mDisplay, ActivityDisplay.POSITION_TOP);
}
@Override
@@ -1045,7 +1045,7 @@
@Override
void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
- int ignoreWindowingMode, SparseArray<ActivityDisplay> activityDisplays,
+ int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
int callingUid, boolean allowed) {
lastAllowed = allowed;
super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
diff --git a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
index 283c027..d56c6a6 100644
--- a/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RunningTasksTest.java
@@ -68,9 +68,9 @@
public void testCollectTasksByLastActiveTime() throws Exception {
// Create a number of stacks with tasks (of incrementing active time)
final ActivityStackSupervisor supervisor = mService.mStackSupervisor;
- final SparseArray<ActivityDisplay> displays = new SparseArray<>();
+ final ArrayList<ActivityDisplay> displays = new ArrayList<>();
final ActivityDisplay display = new TestActivityDisplay(supervisor, DEFAULT_DISPLAY);
- displays.put(DEFAULT_DISPLAY, display);
+ displays.add(display);
final int numStacks = 2;
for (int stackIndex = 0; stackIndex < numStacks; stackIndex++) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e37a6c1..d94a5f345 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1713,24 +1713,35 @@
UserManager.DISALLOW_ADD_USER),
eq(true), eq(CAMERA_DISABLED_GLOBALLY));
reset(getServices().userManagerInternal);
+ }
- // Set up another DA and let it disable camera. Now DISALLOW_CAMERA will only be applied
- // locally.
- dpm.setCameraDisabled(admin1, false);
- reset(getServices().userManagerInternal);
+ public void testDaDisallowedPolicies_SecurityException() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
- setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
- dpm.setActiveAdmin(admin2, /* replace =*/ false, UserHandle.USER_SYSTEM);
- dpm.setCameraDisabled(admin2, true);
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
- verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
- eq(UserHandle.USER_SYSTEM),
- // DISALLOW_CAMERA will be applied to both local and global. <- TODO: fix this
- MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
- UserManager.DISALLOW_ADD_USER),
- eq(true), eq(CAMERA_DISABLED_LOCALLY));
- reset(getServices().userManagerInternal);
- // TODO Make sure restrictions are written to the file.
+ boolean originalCameraDisabled = dpm.getCameraDisabled(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setCameraDisabled(admin1, true));
+ assertEquals(originalCameraDisabled, dpm.getCameraDisabled(admin1));
+
+ int originalKeyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setKeyguardDisabledFeatures(admin1,
+ DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL));
+ assertEquals(originalKeyguardDisabledFeatures, dpm.getKeyguardDisabledFeatures(admin1));
+
+ long originalPasswordExpirationTimeout = dpm.getPasswordExpirationTimeout(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPasswordExpirationTimeout(admin1, 1234));
+ assertEquals(originalPasswordExpirationTimeout, dpm.getPasswordExpirationTimeout(admin1));
+
+ int originalPasswordQuality = dpm.getPasswordQuality(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC));
+ assertEquals(originalPasswordQuality, dpm.getPasswordQuality(admin1));
}
public void testSetUserRestriction_asPo() {
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index ece9f42..79a654b 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -52,6 +52,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -82,6 +84,8 @@
private static HandlerThread sThread =
new HandlerThread("brightness.test", android.os.Process.THREAD_PRIORITY_BACKGROUND);
+ private int mDefaultNightModeColorTemperature;
+
private static Handler ensureHandler() {
synchronized (sHandlerLock) {
if (sHandler == null) {
@@ -98,6 +102,9 @@
mInjector = new TestInjector(ensureHandler());
mTracker = new BrightnessTracker(InstrumentationRegistry.getContext(), mInjector);
+ mDefaultNightModeColorTemperature =
+ InstrumentationRegistry.getContext().getResources().getInteger(
+ R.integer.config_nightDisplayColorTemperatureDefault);
}
@Test
@@ -188,7 +195,7 @@
// System had no data so these should all be at defaults.
assertEquals(Float.NaN, event.batteryLevel, 0.0);
assertFalse(event.nightMode);
- assertEquals(0, event.colorTemperature);
+ assertEquals(mDefaultNightModeColorTemperature, event.colorTemperature);
}
@Test
@@ -863,5 +870,17 @@
public boolean isInteractive(Context context) {
return mInteractive;
}
+
+ @Override
+ public int getColorTemperature(Context context, int userId) {
+ return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
+ mDefaultNightModeColorTemperature);
+ }
+
+ @Override
+ public boolean isNightModeActive(Context context, int userId) {
+ return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
+ 0) == 1;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 0d40c5e..b330304 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -446,24 +446,6 @@
assertEquals(anotherAlwaysOnTopStack, mDisplayContent.getStacks().get(topPosition - 1));
}
- /**
- * Test that WM does not report displays to AM that are pending to be removed.
- */
- @Test
- public void testDontReportDeferredRemoval() {
- // Create a display and add an animating window to it.
- final DisplayContent dc = createNewDisplay();
- final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
- window.mAnimatingExit = true;
- // Request display removal, it should be deferred.
- dc.removeIfPossible();
- // Request ordered display ids from WM.
- final SparseIntArray orderedDisplayIds = new SparseIntArray();
- sWm.getDisplaysInFocusOrder(orderedDisplayIds);
- // Make sure that display that is marked for removal is not reported.
- assertEquals(-1, orderedDisplayIds.indexOfValue(dc.getDisplayId()));
- }
-
@Test
public void testDisplayCutout_rot0() throws Exception {
synchronized (sWm.getWindowManagerLock()) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index ea3a3d0..e648230 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -133,15 +133,15 @@
}
private void assertContentInset(WindowState w, int left, int top, int right, int bottom) {
- assertRect(w.mContentInsets, left, top, right, bottom);
+ assertRect(w.getContentInsets(), left, top, right, bottom);
}
private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) {
- assertRect(w.mVisibleInsets, left, top, right, bottom);
+ assertRect(w.getVisibleInsets(), left, top, right, bottom);
}
private void assertStableInset(WindowState w, int left, int top, int right, int bottom) {
- assertRect(w.mStableInsets, left, top, right, bottom);
+ assertRect(w.getStableInsets(), left, top, right, bottom);
}
private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 5a42a84..b43d9a6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -50,13 +50,19 @@
import static org.mockito.Mockito.verify;
import android.graphics.Matrix;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.util.Size;
+import android.view.DisplayCutout;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import com.android.server.wm.utils.WmDisplayCutout;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.LinkedList;
import androidx.test.filters.FlakyTest;
@@ -382,6 +388,20 @@
}
}
+ @Test
+ public void testDisplayCutoutIsCalculatedRelativeToFrame() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ WindowFrames wf = new WindowFrames();
+ wf.mParentFrame.set(7, 10, 185, 380);
+ wf.mDisplayFrame.set(wf.mParentFrame);
+ final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22),
+ Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400)));
+ wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
+
+ app.computeFrameLw(wf);
+ assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20)));
+ }
+
private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
reset(mPowerManagerWrapper);
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
diff --git a/services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java b/services/tests/servicestests/utils/com/android/server/testutils/OffsettableClock.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/testutils/OffsettableClock.java
rename to services/tests/servicestests/utils/com/android/server/testutils/OffsettableClock.java
diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
rename to services/tests/servicestests/utils/com/android/server/testutils/TestHandler.java
diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestUtils.java b/services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/testutils/TestUtils.java
rename to services/tests/servicestests/utils/com/android/server/testutils/TestUtils.java
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0ff124e..a1b3b98 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -65,6 +65,8 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.Application;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
@@ -195,6 +197,8 @@
IUriGrantsManager mUgm;
@Mock
UriGrantsManagerInternal mUgmInternal;
+ @Mock
+ AppOpsManager mAppOpsManager;
// Use a Testable subclass so we can simulate calls from the system without failing.
private static class TestableNotificationManagerService extends NotificationManagerService {
@@ -295,7 +299,8 @@
mListeners, mAssistants, mConditionProviders,
mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
mGroupHelper, mAm, mAppUsageStats,
- mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal);
+ mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
+ mAppOpsManager);
} catch (SecurityException e) {
if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
throw e;
@@ -531,7 +536,7 @@
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(channel)));
final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -549,7 +554,7 @@
final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -578,7 +583,7 @@
StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
// The first time a foreground service notification is shown, we allow the channel
@@ -600,7 +605,7 @@
sbn = generateNotificationRecord(channel).sbn;
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
// The second time it is shown, we keep the user's preference.
@@ -631,7 +636,7 @@
mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -645,7 +650,7 @@
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -667,7 +672,7 @@
final StatusBarNotification sbn =
generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
sbn.getNotification().category = category;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
}
waitForIdle();
@@ -691,7 +696,7 @@
final StatusBarNotification sbn =
generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
sbn.getNotification().category = category;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
}
waitForIdle();
@@ -714,7 +719,7 @@
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().category = category;
try {
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
fail("Calls from non system apps should not allow use of restricted categories");
} catch (SecurityException e) {
@@ -746,7 +751,7 @@
@Test
public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
@@ -756,7 +761,7 @@
@Test
public void testCancelNotificationImmediatelyAfterEnqueue() throws Exception {
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
mBinderService.cancelNotificationWithTag(PKG, "tag", 0, 0);
waitForIdle();
@@ -768,10 +773,10 @@
@Test
public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
waitForIdle();
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
generateNotificationRecord(null).getNotification(), 0);
mBinderService.cancelNotificationWithTag(PKG, "tag", 0, 0);
waitForIdle();
@@ -788,7 +793,7 @@
public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
NotificationRecord r = generateNotificationRecord(null);
final StatusBarNotification sbn = r.sbn;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelNotificationsFromListener(null, null);
waitForIdle();
@@ -801,7 +806,7 @@
@Test
public void testCancelAllNotificationsImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
@@ -816,7 +821,7 @@
final NotificationRecord n = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId());
waitForIdle();
@@ -839,9 +844,9 @@
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group1", false);
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
waitForIdle();
@@ -854,7 +859,7 @@
public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
for (int i = 0; i < 10; i++) {
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
}
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
@@ -873,17 +878,17 @@
mTestNotificationChannel, 2, "group1", false);
// fully post parent notification
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
waitForIdle();
// enqueue the child several times
for (int i = 0; i < 10; i++) {
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
}
// make the parent a child, which will cancel the child notification
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
parentAsChild.sbn.getUserId());
waitForIdle();
@@ -895,7 +900,7 @@
public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
@@ -909,7 +914,7 @@
public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications("other_pkg_name", sbn.getUserId());
waitForIdle();
@@ -922,7 +927,7 @@
@Test
public void testCancelAllNotifications_NullPkgRemovesAll() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(null, sbn.getUserId());
waitForIdle();
@@ -935,7 +940,7 @@
@Test
public void testCancelAllNotifications_NullPkgIgnoresUserAllNotifications() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), UserHandle.USER_ALL);
// Null pkg is how we signal a user switch.
mBinderService.cancelAllNotifications(null, sbn.getUserId());
@@ -950,7 +955,7 @@
public void testAppInitiatedCancelAllNotifications_CancelsNoClearFlag() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= Notification.FLAG_NO_CLEAR;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
@@ -1037,7 +1042,7 @@
public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mInternalService.removeForegroundServiceFlagFromNotification(PKG, sbn.getId(),
sbn.getUserId());
@@ -1052,10 +1057,10 @@
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags =
Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
waitForIdle();
@@ -1145,21 +1150,21 @@
// should not be returned
final NotificationRecord group2 = generateNotificationRecord(
mTestNotificationChannel, 2, "group2", true);
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
group2.sbn.getId(), group2.sbn.getNotification(), group2.sbn.getUserId());
waitForIdle();
// should not be returned
final NotificationRecord nonGroup = generateNotificationRecord(
mTestNotificationChannel, 3, null, false);
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
nonGroup.sbn.getId(), nonGroup.sbn.getNotification(), nonGroup.sbn.getUserId());
waitForIdle();
// same group, child, should be returned
final NotificationRecord group1Child = generateNotificationRecord(
mTestNotificationChannel, 4, "group1", false);
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null, group1Child.sbn.getId(),
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null, group1Child.sbn.getId(),
group1Child.sbn.getNotification(), group1Child.sbn.getUserId());
waitForIdle();
@@ -1216,7 +1221,7 @@
public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
@@ -1333,7 +1338,7 @@
new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
generateNotificationRecord(null, tv).getNotification(), 0);
verify(mPreferencesHelper, times(1)).getNotificationChannel(
anyString(), anyInt(), eq("foo"), anyBoolean());
@@ -1348,7 +1353,7 @@
mTestNotificationChannel);
Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
generateNotificationRecord(null, tv).getNotification(), 0);
verify(mPreferencesHelper, times(1)).getNotificationChannel(
anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
@@ -1879,7 +1884,7 @@
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
waitForIdle();
@@ -1892,7 +1897,7 @@
final NotificationRecord record = generateNotificationRecord(
mTestNotificationChannel, 2, null, false);
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
record.sbn.getId(), record.sbn.getNotification(), record.sbn.getUserId());
waitForIdle();
@@ -1904,7 +1909,7 @@
final NotificationRecord parent = generateNotificationRecord(
mTestNotificationChannel, 2, "group", true);
- mBinderService.enqueueNotificationWithTag(PKG, "opPkg", null,
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
waitForIdle();
@@ -2378,12 +2383,12 @@
@Test
public void testBumpFGImportance_noChannelChangePreOApp() throws Exception {
String preOPkg = PKG_N_MR1;
- int preOUid = 145;
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
when(mPackageManagerClient.getApplicationInfoAsUser(eq(preOPkg), anyInt(), anyInt()))
.thenReturn(legacy);
- when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt())).thenReturn(preOUid);
+ when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt()))
+ .thenReturn(Binder.getCallingUid());
getContext().setMockPackageManager(mPackageManagerClient);
Notification.Builder nb = new Notification.Builder(mContext,
@@ -2393,12 +2398,13 @@
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
- StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
- 0, nb.build(), new UserHandle(preOUid), null, 0);
+ StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag",
+ Binder.getCallingUid(), 0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
- mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag",
- sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), sbn.getOpPkg(),
+ sbn.getTag(), sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
+
assertEquals(IMPORTANCE_LOW,
mService.getNotificationRecord(sbn.getKey()).getImportance());
@@ -2408,8 +2414,8 @@
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
- sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", preOUid,
- 0, nb.build(), new UserHandle(preOUid), null, 0);
+ sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", Binder.getCallingUid(),
+ 0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -3360,7 +3366,7 @@
}
@Test
- public void testMybeRecordInterruptionLocked_doesNotRecordTwice()
+ public void testMaybeRecordInterruptionLocked_doesNotRecordTwice()
throws RemoteException {
final NotificationRecord r = generateNotificationRecord(
mTestNotificationChannel, 1, null, true);
@@ -3373,4 +3379,78 @@
verify(mAppUsageStats, times(1)).reportInterruptiveNotification(
anyString(), anyString(), anyInt());
}
+
+ @Test
+ public void testResolveNotificationUid_sameApp() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.uid = Binder.getCallingUid();
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+
+ int actualUid = mService.resolveNotificationUid("caller", "caller", info.uid, 0);
+
+ assertEquals(info.uid, actualUid);
+ }
+
+ @Test
+ public void testResolveNotificationUid_sameAppWrongPkg() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.uid = Binder.getCallingUid();
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+
+ try {
+ mService.resolveNotificationUid("caller", "other", info.uid, 0);
+ fail("Incorrect pkg didn't throw security exception");
+ } catch (SecurityException e) {
+ // yay
+ }
+ }
+
+ @Test
+ public void testResolveNotificationUid_sameAppWrongUid() throws Exception {
+ ApplicationInfo info = new ApplicationInfo();
+ info.uid = 1356347;
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info);
+
+ try {
+ mService.resolveNotificationUid("caller", "caller", 9, 0);
+ fail("Incorrect uid didn't throw security exception");
+ } catch (SecurityException e) {
+ // yay
+ }
+ }
+
+ @Test
+ public void testResolveNotificationUid_delegateAllowed() throws Exception {
+ int expectedUid = 123;
+
+ when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(expectedUid);
+ mService.setPreferencesHelper(mPreferencesHelper);
+ when(mPreferencesHelper.isDelegateAllowed(anyString(), anyInt(), anyString(), anyInt()))
+ .thenReturn(true);
+
+ assertEquals(expectedUid, mService.resolveNotificationUid("caller", "target", 9, 0));
+ }
+
+ @Test
+ public void testResolveNotificationUid_androidAllowed() throws Exception {
+ int expectedUid = 123;
+
+ when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(expectedUid);
+ // no delegate
+
+ assertEquals(expectedUid, mService.resolveNotificationUid("android", "target", 0, 0));
+ }
+
+ @Test
+ public void testResolveNotificationUid_delegateNotAllowed() throws Exception {
+ when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(123);
+ // no delegate
+
+ try {
+ mService.resolveNotificationUid("caller", "target", 9, 0);
+ fail("Incorrect uid didn't throw security exception");
+ } catch (SecurityException e) {
+ // yay
+ }
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 73adf25..750345b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -123,7 +123,6 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- UserHandle user = UserHandle.ALL;
final ApplicationInfo legacy = new ApplicationInfo();
legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -176,11 +175,6 @@
.build();
}
- private NotificationChannel getDefaultChannel() {
- return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
- IMPORTANCE_LOW);
- }
-
private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, boolean forBackup,
String... channelIds)
throws Exception {
@@ -1787,4 +1781,159 @@
mHelper.setEnabled(PKG_N_MR1, 1000, true);
assertEquals(3, mHelper.getBlockedAppCount(0));
}
+
+ @Test
+ public void testSetNotificationDelegate() {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testRevokeNotificationDelegate() {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testRevokeNotificationDelegate_noDelegateExistsNoCrash() {
+ mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testToggleNotificationDelegate() {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
+ assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testToggleNotificationDelegate_noDelegateExistsNoCrash() {
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testIsDelegateAllowed_noSource() {
+ assertFalse(mHelper.isDelegateAllowed("does not exist", -1, "whatever", 0));
+ }
+
+ @Test
+ public void testIsDelegateAllowed_noDelegate() {
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
+
+ assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "whatever", 0));
+ }
+
+ @Test
+ public void testIsDelegateAllowed_delegateDisabledByApp() {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+ assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "other", 53));
+ }
+
+ @Test
+ public void testIsDelegateAllowed_wrongDelegate() {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+ assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "banana", 27));
+ }
+
+ @Test
+ public void testIsDelegateAllowed_delegateDisabledByUser() {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+
+ assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "other", 53));
+ }
+
+ @Test
+ public void testIsDelegateAllowed() {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+
+ assertTrue(mHelper.isDelegateAllowed(PKG_O, UID_O, "other", 53));
+ }
+
+ @Test
+ public void testDelegateXml_noDelegate() throws Exception {
+ mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testDelegateXml_delegate() throws Exception {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testDelegateXml_disabledDelegate() throws Exception {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testDelegateXml_userDisabledDelegate() throws Exception {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ // appears disabled
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+ // but was loaded and can be toggled back on
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
+ assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
+
+ @Test
+ public void testDelegateXml_entirelyDisabledDelegate() throws Exception {
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, false);
+ mHelper.revokeNotificationDelegate(PKG_O, UID_O);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false);
+ mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper);
+ loadStreamXml(baos, false);
+
+ // appears disabled
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+ mHelper.setNotificationDelegate(PKG_O, UID_O, "other", 53);
+ assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O));
+
+ mHelper.toggleNotificationDelegate(PKG_O, UID_O, true);
+ assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O));
+ }
}
diff --git a/services/tests/wmtests/Android.mk b/services/tests/wmtests/Android.mk
new file mode 100644
index 0000000..0f8b18a
--- /dev/null
+++ b/services/tests/wmtests/Android.mk
@@ -0,0 +1,41 @@
+#########################################################################
+# Build WmTests package
+#########################################################################
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-java-files-under, ../servicestests/utils)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ androidx-test \
+ mockito-target-minus-junit4 \
+ platform-test-annotations \
+
+LOCAL_JAVA_LIBRARIES := \
+ android.test.mock \
+ android.test.base \
+ android.test.runner \
+
+LOCAL_PACKAGE_NAME := WmTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
new file mode 100644
index 0000000..1fb9473
--- /dev/null
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.wmtests">
+
+ <!-- Uses API introduced in P (28) -->
+ <uses-sdk
+ android:minSdkVersion="1"
+ android:targetSdkVersion="28" />
+
+ <application android:testOnly="true" />
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Window Manager Tests"
+ android:targetPackage="com.android.frameworks.wmtests" />
+</manifest>
diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml
new file mode 100644
index 0000000..2717ef90
--- /dev/null
+++ b/services/tests/wmtests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<configuration description="Runs Window Manager Tests.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="WmTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="WmTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.frameworks.wmtests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java b/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java
new file mode 100644
index 0000000..023e4ab
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/am/DummyAmTests.java
@@ -0,0 +1,46 @@
+/*
+ * 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.am;
+
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import androidx.test.filters.FlakyTest;
+
+/**
+ * Dummy test for com.android.server.am.
+ * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
+ */
+public class DummyAmTests {
+
+ @Presubmit
+ @Test
+ public void preSubmitTest() {}
+
+ @FlakyTest
+ @Presubmit
+ @Test
+ public void flakyPreSubmitTest() {}
+
+ @Test
+ public void postSubmitTest() {}
+
+ @FlakyTest
+ @Test
+ public void flakyPostSubmitTest() {}
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java b/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java
new file mode 100644
index 0000000..aecb278
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DummyWmTests.java
@@ -0,0 +1,46 @@
+/*
+ * 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.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+
+import androidx.test.filters.FlakyTest;
+
+/**
+ * Dummy test for com.android.server.wm
+ * TODO(b/113800711): Remove this class once the actual tests are moved from servicestests.
+ */
+public class DummyWmTests {
+
+ @Presubmit
+ @Test
+ public void preSubmitTest() {}
+
+ @FlakyTest
+ @Presubmit
+ @Test
+ public void flakyPreSubmitTest() {}
+
+ @Test
+ public void postSubmitTest() {}
+
+ @FlakyTest
+ @Test
+ public void flakyPostSubmitTest() {}
+}
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index e7c54d8..5916b04c 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -168,6 +168,7 @@
}
/** Returns an existing UserData object for the given userId, or creates one */
+ @GuardedBy("mLock")
private UserData getOrCreateUserDataLocked(int userId) {
UserData userData = mUsers.get(userId);
if (userData == null) {
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 2f1c516..4874bce 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -44,6 +44,10 @@
private static final String TAG = UsbAlsaManager.class.getSimpleName();
private static final boolean DEBUG = false;
+ // Flag to turn on/off multi-peripheral select mode
+ // Set to true to have single-device-only mode
+ private static final boolean mIsSingleMode = true;
+
private static final String ALSA_DIRECTORY = "/dev/snd/";
private final Context mContext;
@@ -84,10 +88,11 @@
*/
private synchronized void selectAlsaDevice(UsbAlsaDevice alsaDevice) {
if (DEBUG) {
- Slog.d(TAG, "selectAlsaDevice " + alsaDevice);
+ Slog.d(TAG, "selectAlsaDevice() " + alsaDevice);
}
- if (mSelectedDevice != null) {
+ // This must be where an existing USB audio device is deselected.... (I think)
+ if (mIsSingleMode && mSelectedDevice != null) {
deselectAlsaDevice();
}
@@ -104,9 +109,15 @@
mSelectedDevice = alsaDevice;
alsaDevice.start();
+ if (DEBUG) {
+ Slog.d(TAG, "selectAlsaDevice() - done.");
+ }
}
private synchronized void deselectAlsaDevice() {
+ if (DEBUG) {
+ Slog.d(TAG, "deselectAlsaDevice() mSelectedDevice " + mSelectedDevice);
+ }
if (mSelectedDevice != null) {
mSelectedDevice.stop();
mSelectedDevice = null;
@@ -133,7 +144,7 @@
/* package */ UsbAlsaDevice selectDefaultDevice() {
if (DEBUG) {
- Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()");
+ Slog.d(TAG, "selectDefaultDevice()");
}
if (mAlsaDevices.size() > 0) {
@@ -230,6 +241,8 @@
}
}
+ logDevices("deviceAdded()");
+
if (DEBUG) {
Slog.d(TAG, "deviceAdded() - done");
}
@@ -254,6 +267,9 @@
Slog.i(TAG, "USB MIDI Device Removed: " + usbMidiDevice);
IoUtils.closeQuietly(usbMidiDevice);
}
+
+ logDevices("usbDeviceRemoved()");
+
}
/* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
@@ -296,6 +312,7 @@
/**
* Dump the USB alsa state.
*/
+ // invoked with "adb shell dumpsys usb"
public void dump(DualDumpOutputStream dump, String idName, long id) {
long token = dump.start(idName, id);
@@ -314,29 +331,26 @@
dump.end(token);
}
-/*
public void logDevicesList(String title) {
- if (DEBUG) {
- for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
- Slog.i(TAG, "UsbDevice-------------------");
- Slog.i(TAG, "" + (entry != null ? entry.getKey() : "[none]"));
- Slog.i(TAG, "UsbAlsaDevice--------------");
- Slog.i(TAG, "" + entry.getValue());
- }
- }
+ if (DEBUG) {
+ Slog.i(TAG, title + "----------------");
+ for (UsbAlsaDevice alsaDevice : mAlsaDevices) {
+ Slog.i(TAG, " -->");
+ Slog.i(TAG, "" + alsaDevice);
+ Slog.i(TAG, " <--");
+ }
+ Slog.i(TAG, "----------------");
+ }
}
-*/
// This logs a more terse (and more readable) version of the devices list
-/*
public void logDevices(String title) {
- if (DEBUG) {
- Slog.i(TAG, title);
- for (HashMap.Entry<UsbDevice,UsbAlsaDevice> entry : mAudioDevices.entrySet()) {
- Slog.i(TAG, entry.getValue().toShortString());
- }
- }
+ if (DEBUG) {
+ Slog.i(TAG, title + "----------------");
+ for (UsbAlsaDevice alsaDevice : mAlsaDevices) {
+ Slog.i(TAG, alsaDevice.toShortString());
+ }
+ Slog.i(TAG, "----------------");
+ }
}
-*/
-
}
diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
new file mode 100644
index 0000000..1730d8f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
@@ -0,0 +1,135 @@
+/*
+ * 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.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * UsbResolveActivityManager creates UI dialogs for user to pick or confirm handler for
+ * usb attach event.
+ *
+ * @hide
+ */
+class UsbHandlerManager {
+ private static final String LOG_TAG = UsbHandlerManager.class.getSimpleName();
+
+ private final Context mContext;
+
+ UsbHandlerManager(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Shows dialog to user to allow them to optionally visit that URL for more
+ * information or software downloads if the attached USB accessory has a valid
+ * URL associated with it.
+ *
+ * @param accessory The accessory to confirm in the UI dialog
+ * @param user The user to start the UI dialog
+ */
+ void showUsbAccessoryUriActivity(@NonNull UsbAccessory accessory,
+ @NonNull UserHandle user) {
+ String uri = accessory.getUri();
+ if (uri != null && uri.length() > 0) {
+ // display URI to user
+ Intent dialogIntent = createDialogIntent();
+ dialogIntent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbAccessoryUriActivity");
+ dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ dialogIntent.putExtra("uri", uri);
+ try {
+ mContext.startActivityAsUser(dialogIntent, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(LOG_TAG, "unable to start UsbAccessoryUriActivity");
+ }
+ }
+ }
+
+ /**
+ * Shows dialog to user to confirm the package to start when the USB device
+ * or accessory is attached and there is only one package claims to handle this
+ * USB device or accessory.
+ *
+ * @param rInfo The ResolveInfo of the package to confirm in the UI dialog
+ * @param device The USB device to confirm
+ * @param accessory The USB accessory to confirm
+ */
+ void confirmUsbHandler(@NonNull ResolveInfo rInfo, @Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory) {
+ Intent resolverIntent = createDialogIntent();
+ // start UsbConfirmActivity if there is only one choice
+ resolverIntent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbConfirmActivity");
+ resolverIntent.putExtra("rinfo", rInfo);
+ UserHandle user =
+ UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
+
+ if (device != null) {
+ resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ } else {
+ resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ }
+
+ try {
+ mContext.startActivityAsUser(resolverIntent, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e);
+ }
+ }
+
+ /**
+ * Shows dialog to user to select the package to start when the USB device
+ * or accessory is attached and there are more than one package claim to handle this
+ * USB device or accessory.
+ *
+ * @param matches The available resolutions of the intent
+ * @param user The user to start UI dialog
+ * @param intent The intent to start the UI dialog
+ */
+ void selectUsbHandler(@NonNull ArrayList<ResolveInfo> matches,
+ @NonNull UserHandle user, @NonNull Intent intent) {
+ Intent resolverIntent = createDialogIntent();
+ resolverIntent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbResolverActivity");
+ resolverIntent.putParcelableArrayListExtra("rlist", matches);
+ resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
+
+ try {
+ mContext.startActivityAsUser(resolverIntent, user);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(LOG_TAG, "unable to start activity " + resolverIntent, e);
+ }
+ }
+
+ private Intent createDialogIntent() {
+ Intent intent = new Intent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 67ad090..589bcdc 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -408,11 +408,15 @@
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
private void usbDeviceRemoved(String deviceAddress) {
+ if (DEBUG) {
+ Slog.d(TAG, "usbDeviceRemoved(" + deviceAddress + ") end");
+ }
+
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceAddress);
if (device != null) {
Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
- mUsbAlsaManager.usbDeviceRemoved(deviceAddress/*device*/);
+ mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
mSettingsManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
new file mode 100644
index 0000000..2c9ee36
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
@@ -0,0 +1,246 @@
+/*
+ * 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.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.Binder;
+import android.os.Process;
+import android.os.UserHandle;
+import android.service.usb.UsbSettingsAccessoryPermissionProto;
+import android.service.usb.UsbSettingsDevicePermissionProto;
+import android.service.usb.UsbUserSettingsManagerProto;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import java.util.HashMap;
+
+/**
+ * UsbPermissionManager manages usb device or accessory access permissions.
+ *
+ * @hide
+ */
+class UsbPermissionManager {
+ private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
+
+ @GuardedBy("mLock")
+ /** Temporary mapping USB device name to list of UIDs with permissions for the device*/
+ private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
+ new HashMap<>();
+ @GuardedBy("mLock")
+ /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/
+ private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
+ new HashMap<>();
+
+ private final UserHandle mUser;
+ private final boolean mDisablePermissionDialogs;
+
+ private final Object mLock = new Object();
+
+ UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) {
+ mUser = user;
+ mDisablePermissionDialogs = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+ }
+
+ /**
+ * Removes access permissions of all packages for the USB accessory.
+ *
+ * @param accessory to remove permissions for
+ */
+ void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
+ synchronized (mLock) {
+ mAccessoryPermissionMap.remove(accessory);
+ }
+ }
+
+ /**
+ * Removes access permissions of all packages for the USB device.
+ *
+ * @param device to remove permissions for
+ */
+ void removeDevicePermissions(@NonNull UsbDevice device) {
+ synchronized (mLock) {
+ mDevicePermissionMap.remove(device.getDeviceName());
+ }
+ }
+
+ /**
+ * Grants permission for USB device without showing system dialog for package with uid.
+ *
+ * @param device to grant permission for
+ * @param uid to grant permission for
+ */
+ void grantDevicePermission(@NonNull UsbDevice device, int uid) {
+ synchronized (mLock) {
+ String deviceName = device.getDeviceName();
+ SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+ if (uidList == null) {
+ uidList = new SparseBooleanArray(1);
+ mDevicePermissionMap.put(deviceName, uidList);
+ }
+ uidList.put(uid, true);
+ }
+ }
+
+ /**
+ * Grants permission for USB accessory without showing system dialog for package with uid.
+ *
+ * @param accessory to grant permission for
+ * @param uid to grant permission for
+ */
+ void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
+ synchronized (mLock) {
+ SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+ if (uidList == null) {
+ uidList = new SparseBooleanArray(1);
+ mAccessoryPermissionMap.put(accessory, uidList);
+ }
+ uidList.put(uid, true);
+ }
+ }
+
+ /**
+ * Returns true if package with uid has permission to access the device.
+ *
+ * @param device to check permission for
+ * @param uid to check permission for
+ * @return {@code true} if package with uid has permission
+ */
+ boolean hasPermission(@NonNull UsbDevice device, int uid) {
+ synchronized (mLock) {
+ if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+ return true;
+ }
+ SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
+ if (uidList == null) {
+ return false;
+ }
+ return uidList.get(uid);
+ }
+ }
+
+ /**
+ * Returns true if caller has permission to access the accessory.
+ *
+ * @param accessory to check permission for
+ * @return {@code true} if caller has permssion
+ */
+ boolean hasPermission(@NonNull UsbAccessory accessory) {
+ synchronized (mLock) {
+ int uid = Binder.getCallingUid();
+ if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+ return true;
+ }
+ SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+ if (uidList == null) {
+ return false;
+ }
+ return uidList.get(uid);
+ }
+ }
+
+ /**
+ * Creates UI dialog to request permission for the given package to access the device
+ * or accessory.
+ *
+ * @param device The USB device attached
+ * @param accessory The USB accessory attached
+ * @param canBeDefault Whether the calling pacakge can set as default handler
+ * of the USB device or accessory
+ * @param packageName The package name of the calling package
+ * @param uid The uid of the calling package
+ * @param userContext The context to start the UI dialog
+ * @param pi PendingIntent for returning result
+ */
+ void requestPermissionDialog(@Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory,
+ boolean canBeDefault,
+ @NonNull String packageName,
+ int uid,
+ @NonNull Context userContext,
+ @NonNull PendingIntent pi) {
+ long identity = Binder.clearCallingIdentity();
+ Intent intent = new Intent();
+ if (device != null) {
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ } else {
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ }
+ intent.putExtra(Intent.EXTRA_INTENT, pi);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
+ intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
+ intent.setClassName("com.android.systemui",
+ "com.android.systemui.usb.UsbPermissionActivity");
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ try {
+ userContext.startActivityAsUser(intent, mUser);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ void dump(@NonNull DualDumpOutputStream dump) {
+ synchronized (mLock) {
+ for (String deviceName : mDevicePermissionMap.keySet()) {
+ long devicePermissionToken = dump.start("device_permissions",
+ UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
+
+ dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
+
+ SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+ int count = uidList.size();
+ for (int i = 0; i < count; i++) {
+ dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
+ }
+
+ dump.end(devicePermissionToken);
+ }
+
+ for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
+ long accessoryPermissionToken = dump.start("accessory_permissions",
+ UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
+
+ dump.write("accessory_description",
+ UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+ accessory.getDescription());
+
+ SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+ int count = uidList.size();
+ for (int i = 0; i < count; i++) {
+ dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
+ }
+
+ dump.end(accessoryPermissionToken);
+ }
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 7a906d0..1ab1f7e 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -193,6 +193,8 @@
MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+ private final UsbHandlerManager mUsbHandlerManager;
+
private final MtpNotificationManager mMtpNotificationManager;
/**
@@ -201,9 +203,11 @@
* @param context The context of the service
* @param user The parent profile
* @param settingsManager The settings manager of the service
+ * @param usbResolveActivityManager The resovle activity manager of the service
*/
UsbProfileGroupSettingsManager(@NonNull Context context, @NonNull UserHandle user,
- @NonNull UsbSettingsManager settingsManager) {
+ @NonNull UsbSettingsManager settingsManager,
+ @NonNull UsbHandlerManager usbResolveActivityManager) {
if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
Context parentUserContext;
@@ -238,6 +242,8 @@
parentUserContext,
device -> resolveActivity(createDeviceAttachedIntent(device),
device, false /* showMtpNotification */));
+
+ mUsbHandlerManager = usbResolveActivityManager;
}
/**
@@ -830,23 +836,8 @@
// don't show the resolver activity if there are no choices available
if (matches.size() == 0) {
if (accessory != null) {
- String uri = accessory.getUri();
- if (uri != null && uri.length() > 0) {
- // display URI to user
- Intent dialogIntent = new Intent();
- dialogIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbAccessoryUriActivity");
- dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- dialogIntent.putExtra("uri", uri);
- try {
- mContext.startActivityAsUser(dialogIntent, mParentUser);
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "unable to start UsbAccessoryUriActivity");
- }
- }
+ mUsbHandlerManager.showUsbAccessoryUriActivity(accessory, mParentUser);
}
-
// do nothing
return;
}
@@ -875,37 +866,10 @@
Slog.e(TAG, "startActivity failed", e);
}
} else {
- Intent resolverIntent = new Intent();
- resolverIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- UserHandle user;
-
if (matches.size() == 1) {
- ResolveInfo rInfo = matches.get(0);
-
- // start UsbConfirmActivity if there is only one choice
- resolverIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbConfirmActivity");
- resolverIntent.putExtra("rinfo", rInfo);
- user = UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
-
- if (device != null) {
- resolverIntent.putExtra(UsbManager.EXTRA_DEVICE, device);
- } else {
- resolverIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- }
+ mUsbHandlerManager.confirmUsbHandler(matches.get(0), device, accessory);
} else {
- user = mParentUser;
-
- // start UsbResolverActivity so user can choose an activity
- resolverIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbResolverActivity");
- resolverIntent.putParcelableArrayListExtra("rlist", matches);
- resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
- }
- try {
- mContext.startActivityAsUser(resolverIntent, user);
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "unable to start activity " + resolverIntent, e);
+ mUsbHandlerManager.selectUsbHandler(matches, mParentUser, intent);
}
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 9221825..27566f0 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -57,10 +57,12 @@
private final SparseArray<UsbProfileGroupSettingsManager> mSettingsByProfileGroup
= new SparseArray<>();
private UserManager mUserManager;
+ private UsbHandlerManager mUsbHandlerManager;
public UsbSettingsManager(@NonNull Context context) {
mContext = context;
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mUsbHandlerManager = new UsbHandlerManager(context);
}
/**
@@ -74,7 +76,8 @@
synchronized (mSettingsByUser) {
UsbUserSettingsManager settings = mSettingsByUser.get(userId);
if (settings == null) {
- settings = new UsbUserSettingsManager(mContext, new UserHandle(userId));
+ settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId),
+ new UsbPermissionManager(mContext, UserHandle.of(userId)));
mSettingsByUser.put(userId, settings);
}
return settings;
@@ -102,7 +105,8 @@
UsbProfileGroupSettingsManager settings = mSettingsByProfileGroup.get(
parentUser.getIdentifier());
if (settings == null) {
- settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this);
+ settings = new UsbProfileGroupSettingsManager(mContext, parentUser, this,
+ mUsbHandlerManager);
mSettingsByProfileGroup.put(parentUser.getIdentifier(), settings);
}
return settings;
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index 24a2d72..fe93399 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -21,15 +21,18 @@
import static com.android.server.usb.UsbProfileGroupSettingsManager.getDeviceFilters;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.res.XmlResourceParser;
import android.hardware.usb.AccessoryFilter;
import android.hardware.usb.DeviceFilter;
import android.hardware.usb.UsbAccessory;
@@ -38,42 +41,34 @@
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Binder;
-import android.os.Process;
import android.os.UserHandle;
import android.service.usb.UsbAccessoryAttachedActivities;
import android.service.usb.UsbDeviceAttachedActivities;
-import android.service.usb.UsbSettingsAccessoryPermissionProto;
-import android.service.usb.UsbSettingsDevicePermissionProto;
import android.service.usb.UsbUserSettingsManagerProto;
import android.util.Slog;
-import android.util.SparseBooleanArray;
+import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
+import org.xmlpull.v1.XmlPullParser;
+
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
class UsbUserSettingsManager {
- private static final String TAG = "UsbUserSettingsManager";
+ private static final String TAG = UsbUserSettingsManager.class.getSimpleName();
private static final boolean DEBUG = false;
private final UserHandle mUser;
- private final boolean mDisablePermissionDialogs;
private final Context mUserContext;
private final PackageManager mPackageManager;
-
- // Temporary mapping USB device name to list of UIDs with permissions for the device
- private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
- new HashMap<>();
- // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
- private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
- new HashMap<>();
+ private final UsbPermissionManager mUsbPermissionManager;
private final Object mLock = new Object();
- public UsbUserSettingsManager(Context context, UserHandle user) {
+ UsbUserSettingsManager(Context context, UserHandle user,
+ @NonNull UsbPermissionManager usbPermissionManager) {
if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
try {
@@ -85,9 +80,7 @@
mPackageManager = mUserContext.getPackageManager();
mUser = user;
-
- mDisablePermissionDialogs = context.getResources().getBoolean(
- com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+ mUsbPermissionManager = usbPermissionManager;
}
/**
@@ -96,9 +89,7 @@
* @param device The device the permissions are for
*/
void removeDevicePermissions(@NonNull UsbDevice device) {
- synchronized (mLock) {
- mDevicePermissionMap.remove(device.getDeviceName());
- }
+ mUsbPermissionManager.removeDevicePermissions(device);
}
/**
@@ -107,9 +98,7 @@
* @param accessory The accessory the permissions are for
*/
void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
- synchronized (mLock) {
- mAccessoryPermissionMap.remove(accessory);
- }
+ mUsbPermissionManager.removeAccessoryPermissions(accessory);
}
/**
@@ -170,35 +159,17 @@
}
public boolean hasPermission(UsbDevice device, String packageName, int uid) {
- synchronized (mLock) {
- if (isCameraDevicePresent(device)) {
- if (!isCameraPermissionGranted(packageName, uid)) {
- return false;
- }
- }
- if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
- return true;
- }
- SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
- if (uidList == null) {
+ if (isCameraDevicePresent(device)) {
+ if (!isCameraPermissionGranted(packageName, uid)) {
return false;
}
- return uidList.get(uid);
}
+
+ return mUsbPermissionManager.hasPermission(device, uid);
}
public boolean hasPermission(UsbAccessory accessory) {
- synchronized (mLock) {
- int uid = Binder.getCallingUid();
- if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
- return true;
- }
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- if (uidList == null) {
- return false;
- }
- return uidList.get(uid);
- }
+ return mUsbPermissionManager.hasPermission(accessory);
}
public void checkPermission(UsbDevice device, String packageName, int uid) {
@@ -213,7 +184,11 @@
}
}
- private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
+ private void requestPermissionDialog(@Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory,
+ boolean canBeDefault,
+ String packageName,
+ PendingIntent pi) {
final int uid = Binder.getCallingUid();
// compare uid with packageName to foil apps pretending to be someone else
@@ -227,27 +202,15 @@
throw new IllegalArgumentException("package " + packageName + " not found");
}
- long identity = Binder.clearCallingIdentity();
- intent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbPermissionActivity");
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(Intent.EXTRA_INTENT, pi);
- intent.putExtra("package", packageName);
- intent.putExtra(Intent.EXTRA_UID, uid);
- try {
- mUserContext.startActivityAsUser(intent, mUser);
- } catch (ActivityNotFoundException e) {
- Slog.e(TAG, "unable to start UsbPermissionActivity");
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ mUsbPermissionManager.requestPermissionDialog(device,
+ accessory, canBeDefault, packageName, uid, mUserContext, pi);
}
public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
- Intent intent = new Intent();
+ Intent intent = new Intent();
// respond immediately if permission has already been granted
- if (hasPermission(device, packageName, uid)) {
+ if (hasPermission(device, packageName, uid)) {
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
try {
@@ -270,16 +233,13 @@
}
}
- // start UsbPermissionActivity so user can choose an activity
- intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- requestPermissionDialog(intent, packageName, pi);
+ requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi);
}
public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
- Intent intent = new Intent();
-
// respond immediately if permission has already been granted
if (hasPermission(accessory)) {
+ Intent intent = new Intent();
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
try {
@@ -290,31 +250,16 @@
return;
}
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- requestPermissionDialog(intent, packageName, pi);
+ requestPermissionDialog(null, accessory,
+ canBeDefault(accessory, packageName), packageName, pi);
}
public void grantDevicePermission(UsbDevice device, int uid) {
- synchronized (mLock) {
- String deviceName = device.getDeviceName();
- SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
- if (uidList == null) {
- uidList = new SparseBooleanArray(1);
- mDevicePermissionMap.put(deviceName, uidList);
- }
- uidList.put(uid, true);
- }
+ mUsbPermissionManager.grantDevicePermission(device, uid);
}
public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
- synchronized (mLock) {
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- if (uidList == null) {
- uidList = new SparseBooleanArray(1);
- mAccessoryPermissionMap.put(accessory, uidList);
- }
- uidList.put(uid, true);
- }
+ mUsbPermissionManager.grantAccessoryPermission(accessory, uid);
}
/**
@@ -329,42 +274,108 @@
mUser.getIdentifier());
}
+ /**
+ * Can the app be the default for the USB device. I.e. can the app be launched by default if
+ * the device is plugged in.
+ *
+ * @param device The device the app would be default for
+ * @param packageName The package name of the app
+ *
+ * @return {@code true} if the app can be default
+ */
+ private boolean canBeDefault(@NonNull UsbDevice device, String packageName) {
+ ActivityInfo[] activities = getPackageActivities(packageName);
+ if (activities != null) {
+ int numActivities = activities.length;
+ for (int i = 0; i < numActivities; i++) {
+ ActivityInfo activityInfo = activities[i];
+
+ try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager,
+ UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+ if (parser == null) {
+ continue;
+ }
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("usb-device".equals(parser.getName())) {
+ DeviceFilter filter = DeviceFilter.read(parser);
+ if (filter.matches(device)) {
+ return true;
+ }
+ }
+
+ XmlUtils.nextElement(parser);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
+ * the accessory is plugged in.
+ *
+ * @param accessory The accessory the app would be default for
+ * @param packageName The package name of the app
+ *
+ * @return {@code true} if the app can be default
+ */
+ private boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) {
+ ActivityInfo[] activities = getPackageActivities(packageName);
+ if (activities != null) {
+ int numActivities = activities.length;
+ for (int i = 0; i < numActivities; i++) {
+ ActivityInfo activityInfo = activities[i];
+
+ try (XmlResourceParser parser = activityInfo.loadXmlMetaData(mPackageManager,
+ UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+ if (parser == null) {
+ continue;
+ }
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("usb-accessory".equals(parser.getName())) {
+ AccessoryFilter filter = AccessoryFilter.read(parser);
+ if (filter.matches(accessory)) {
+ return true;
+ }
+ }
+
+ XmlUtils.nextElement(parser);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private ActivityInfo[] getPackageActivities(String packageName) {
+ try {
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+ return packageInfo.activities;
+ } catch (PackageManager.NameNotFoundException e) {
+ // ignore
+ }
+ return null;
+ }
+
public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
long token = dump.start(idName, id);
synchronized (mLock) {
dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier());
- for (String deviceName : mDevicePermissionMap.keySet()) {
- long devicePermissionToken = dump.start("device_permissions",
- UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
-
- dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
-
- SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
- int count = uidList.size();
- for (int i = 0; i < count; i++) {
- dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
- }
-
- dump.end(devicePermissionToken);
- }
- for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
- long accessoryPermissionToken = dump.start("accessory_permissions",
- UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
-
- dump.write("accessory_description",
- UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
- accessory.getDescription());
-
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- int count = uidList.size();
- for (int i = 0; i < count; i++) {
- dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
- }
-
- dump.end(accessoryPermissionToken);
- }
+ mUsbPermissionManager.dump(dump);
List<ResolveInfo> deviceAttachedActivities = queryIntentActivities(
new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED));
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index de177169..7c22613 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -670,6 +670,7 @@
/**
* Disconnect from the service, but allow to re-connect when new operations are triggered.
*/
+ @GuardedBy("mRemoteServiceLock")
private void disconnectLocked() {
if (mService != null) {
try {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8703e65..ffbe7d3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -107,13 +107,34 @@
/**
* Boolean indicating if the "Call barring" item is visible in the Call Settings menu.
- * true means visible. false means gone.
- * @hide
+ * If true, the "Call Barring" menu will be visible. If false, the menu will be gone.
+ *
+ * Disabled by default.
*/
public static final String KEY_CALL_BARRING_VISIBILITY_BOOL =
"call_barring_visibility_bool";
/**
+ * Flag indicating whether or not changing the call barring password via the "Call Barring"
+ * settings menu is supported. If true, the option will be visible in the "Call
+ * Barring" settings menu. If false, the option will not be visible.
+ *
+ * Enabled by default.
+ */
+ public static final String KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL =
+ "call_barring_supports_password_change_bool";
+
+ /**
+ * Flag indicating whether or not deactivating all call barring features via the "Call Barring"
+ * settings menu is supported. If true, the option will be visible in the "Call
+ * Barring" settings menu. If false, the option will not be visible.
+ *
+ * Enabled by default.
+ */
+ public static final String KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL =
+ "call_barring_supports_deactivate_all_bool";
+
+ /**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
* If true, this will prevent the IccNetworkDepersonalizationPanel from being shown, and
@@ -2125,6 +2146,8 @@
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONED_BOOL, false);
sDefaults.putBoolean(KEY_CALL_BARRING_VISIBILITY_BOOL, false);
+ sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_PASSWORD_CHANGE_BOOL, true);
+ sDefaults.putBoolean(KEY_CALL_BARRING_SUPPORTS_DEACTIVATE_ALL_BOOL, true);
sDefaults.putBoolean(KEY_CALL_FORWARDING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index bd6a59d..498be96 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -23,6 +23,7 @@
import android.os.Looper;
import android.os.Message;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.IPhoneStateListener;
import java.lang.ref.WeakReference;
@@ -778,8 +779,12 @@
}
}
+ /**
+ * @hide
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@UnsupportedAppUsage
- IPhoneStateListener callback = new IPhoneStateListenerStub(this);
+ public final IPhoneStateListener callback = new IPhoneStateListenerStub(this);
private void log(String s) {
Rlog.d(LOG_TAG, s);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 119733e..1d9e605 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -19,6 +19,7 @@
import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
+import android.annotation.CallbackExecutor;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,6 +48,7 @@
import android.os.ServiceManager;
import android.telephony.euicc.EuiccManager;
import android.util.DisplayMetrics;
+import android.util.Log;
import com.android.internal.telephony.IOnSubscriptionsChangedListener;
import com.android.internal.telephony.ISub;
@@ -57,6 +59,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
/**
@@ -666,7 +669,7 @@
tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
}
} catch (RemoteException ex) {
- // Should not happen
+ Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
}
}
@@ -684,7 +687,7 @@
+ " listener=" + listener);
}
try {
- // We use the TelephonyRegistry as its runs in the system and thus is always
+ // We use the TelephonyRegistry as it runs in the system and thus is always
// available where as SubscriptionController could crash and not be available
ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
@@ -692,7 +695,116 @@
tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
}
} catch (RemoteException ex) {
- // Should not happen
+ Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
+ }
+ }
+
+ /**
+ * A listener class for monitoring changes to {@link SubscriptionInfo} records of opportunistic
+ * subscriptions.
+ * <p>
+ * Override the onOpportunisticSubscriptionsChanged method in the object that extends this
+ * or {@link #addOnOpportunisticSubscriptionsChangedListener(
+ * Executor, OnOpportunisticSubscriptionsChangedListener)}
+ * to register your listener and to unregister invoke
+ * {@link #removeOnOpportunisticSubscriptionsChangedListener(
+ * OnOpportunisticSubscriptionsChangedListener)}
+ * <p>
+ * Permissions android.Manifest.permission.READ_PHONE_STATE is required
+ * for #onOpportunisticSubscriptionsChanged to be invoked.
+ */
+ public static class OnOpportunisticSubscriptionsChangedListener {
+ private Executor mExecutor;
+ /**
+ * Callback invoked when there is any change to any SubscriptionInfo. Typically
+ * this method would invoke {@link #getActiveSubscriptionInfoList}
+ */
+ public void onOpportunisticSubscriptionsChanged() {
+ if (DBG) log("onOpportunisticSubscriptionsChanged: NOT OVERRIDDEN");
+ }
+
+ private void setExecutor(Executor executor) {
+ mExecutor = executor;
+ }
+
+ /**
+ * The callback methods need to be called on the handler thread where
+ * this object was created. If the binder did that for us it'd be nice.
+ */
+ IOnSubscriptionsChangedListener callback = new IOnSubscriptionsChangedListener.Stub() {
+ @Override
+ public void onSubscriptionsChanged() {
+ if (DBG) log("onOpportunisticSubscriptionsChanged callback received.");
+ mExecutor.execute(() -> onOpportunisticSubscriptionsChanged());
+ }
+ };
+
+ private void log(String s) {
+ Rlog.d(LOG_TAG, s);
+ }
+ }
+
+ /**
+ * Register for changes to the list of opportunistic subscription records or to the
+ * individual records themselves. When a change occurs the onOpportunisticSubscriptionsChanged
+ * method of the listener will be invoked immediately if there has been a notification.
+ *
+ * @param listener an instance of {@link OnOpportunisticSubscriptionsChangedListener} with
+ * onOpportunisticSubscriptionsChanged overridden.
+ */
+ public void addOnOpportunisticSubscriptionsChangedListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnOpportunisticSubscriptionsChangedListener listener) {
+ if (executor == null || listener == null) {
+ return;
+ }
+
+ String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (DBG) {
+ logd("register addOnOpportunisticSubscriptionsChangedListener pkgName=" + pkgName
+ + " listener=" + listener);
+ }
+
+ listener.setExecutor(executor);
+
+ try {
+ // We use the TelephonyRegistry as it runs in the system and thus is always
+ // available. Where as SubscriptionController could crash and not be available
+ ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ if (tr != null) {
+ tr.addOnOpportunisticSubscriptionsChangedListener(pkgName, listener.callback);
+ }
+ } catch (RemoteException ex) {
+ Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
+ }
+ }
+
+ /**
+ * Unregister the {@link OnOpportunisticSubscriptionsChangedListener} that is currently
+ * listening opportunistic subscriptions change. This is not strictly necessary
+ * as the listener will automatically be unregistered if an attempt to invoke the listener
+ * fails.
+ *
+ * @param listener that is to be unregistered.
+ */
+ public void removeOnOpportunisticSubscriptionsChangedListener(
+ OnOpportunisticSubscriptionsChangedListener listener) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ if (DBG) {
+ logd("unregister OnOpportunisticSubscriptionsChangedListener pkgForDebug="
+ + pkgForDebug + " listener=" + listener);
+ }
+ try {
+ // We use the TelephonyRegistry as it runs in the system and thus is always
+ // available where as SubscriptionController could crash and not be available
+ ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+ "telephony.registry"));
+ if (tr != null) {
+ tr.removeOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+ }
+ } catch (RemoteException ex) {
+ Log.e(LOG_TAG, "Remote exception ITelephonyRegistry " + ex);
}
}
@@ -1177,6 +1289,15 @@
}
+ /**
+ * Get an array of Subscription Ids for specified slot Index.
+ * @param slotIndex the slot Index.
+ * @return subscription Ids or null if the given slot Index is not valid.
+ */
+ public static int[] getSubscriptionIds(int slotIndex) {
+ return getSubId(slotIndex);
+ }
+
/** @hide */
@UnsupportedAppUsage
public static int[] getSubId(int slotIndex) {
@@ -2007,20 +2128,21 @@
}
/**
- * Get User downloaded Profiles.
+ * Get opportunistic data Profiles.
*
- * Provide all available user downloaded profile on the phone.
- * @param slotId on which phone the switch will operate on
+ * Provide all available user downloaded profiles on phone which are used only for
+ * opportunistic data.
+ * @param slotIndex slot on which the profiles are queried from.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- List<SubscriptionInfo> getOpportunisticSubscriptions(int slotId) {
+ public List<SubscriptionInfo> getOpportunisticSubscriptions(int slotIndex) {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
List<SubscriptionInfo> subInfoList = null;
try {
ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
if (iSub != null) {
- subInfoList = iSub.getOpportunisticSubscriptions(slotId, pkgForDebug);
+ subInfoList = iSub.getOpportunisticSubscriptions(slotIndex, pkgForDebug);
}
} catch (RemoteException ex) {
// ignore it
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index abcdeed..2ac0afe 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -64,6 +64,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telecom.ITelecomService;
import com.android.internal.telephony.CellNetworkScanResult;
+import com.android.internal.telephony.IAnas;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephonyRegistry;
@@ -134,6 +135,22 @@
static final int NEVER_USE = 2;
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"NETWORK_SELECTION_MODE_"},
+ value = {
+ NETWORK_SELECTION_MODE_UNKNOWN,
+ NETWORK_SELECTION_MODE_AUTO,
+ NETWORK_SELECTION_MODE_MANUAL})
+ public @interface NetworkSelectionMode {}
+
+ /** @hide */
+ public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0;
+ /** @hide */
+ public static final int NETWORK_SELECTION_MODE_AUTO = 1;
+ /** @hide */
+ public static final int NETWORK_SELECTION_MODE_MANUAL = 2;
+
/** The otaspMode passed to PhoneStateListener#onOtaspChanged */
/** @hide */
static public final int OTASP_UNINITIALIZED = 0;
@@ -4327,6 +4344,10 @@
return ITelephonyRegistry.Stub.asInterface(ServiceManager.getService("telephony.registry"));
}
+ private IAnas getIAnas() {
+ return IAnas.Stub.asInterface(ServiceManager.getService("ianas"));
+ }
+
//
//
// PhoneStateListener
@@ -5785,6 +5806,31 @@
return false;
}
+ /**
+ * Get the network selection mode.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+
+ * @return the network selection mode.
+ *
+ * @hide
+ */
+ @NetworkSelectionMode
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getNetworkSelectionMode() {
+ int mode = NETWORK_SELECTION_MODE_UNKNOWN;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ mode = telephony.getNetworkSelectionMode(getSubId());
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getNetworkSelectionMode RemoteException", ex);
+ }
+ return mode;
+ }
+
/**
* Set the preferred network type.
* Used for device configuration by some CDMA operators.
@@ -6699,6 +6745,84 @@
}
/**
+ * Gets the roaming mode for CDMA phone.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * @return one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT}, {@link #CDMA_ROAMING_MODE_HOME},
+ * {@link #CDMA_ROAMING_MODE_AFFILIATED}, {@link #CDMA_ROAMING_MODE_ANY}.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public int getCdmaRoamingMode() {
+ int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ mode = telephony.getCdmaRoamingMode(getSubId());
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error calling ITelephony#getCdmaRoamingMode", ex);
+ }
+ return mode;
+ }
+
+ /**
+ * Sets the roaming mode for CDMA phone to the given mode {@code mode}.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
+ * @param mode should be one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT},
+ * {@link #CDMA_ROAMING_MODE_HOME}, {@link #CDMA_ROAMING_MODE_AFFILIATED},
+ * {@link #CDMA_ROAMING_MODE_ANY}.
+ *
+ * @return {@code true} if successed.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setCdmaRoamingMode(int mode) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setCdmaRoamingMode(getSubId(), mode);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error calling ITelephony#setCdmaRoamingMode", ex);
+ }
+ return false;
+ }
+
+ /**
+ * Sets the subscription mode for CDMA phone to the given mode {@code mode}.
+ *
+ * @param mode CDMA subscription mode
+ *
+ * @return {@code true} if successed.
+ *
+ * @see Phone#CDMA_SUBSCRIPTION_UNKNOWN
+ * @see Phone#CDMA_SUBSCRIPTION_RUIM_SIM
+ * @see Phone#CDMA_SUBSCRIPTION_NV
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setCdmaSubscriptionMode(int mode) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setCdmaSubscriptionMode(getSubId(), mode);
+ }
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error calling ITelephony#setCdmaSubscriptionMode", ex);
+ }
+ return false;
+ }
+
+ /**
* Enables/Disables the data roaming on the subscription.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
@@ -8193,7 +8317,6 @@
return UNKNOWN_CARRIER_ID_LIST_VERSION;
}
-
/**
* How many modems can have simultaneous data connections.
* @hide
@@ -8211,4 +8334,62 @@
}
return 0;
}
+
+ /**
+ * Enable or disable AlternativeNetworkAccessService.
+ *
+ * This method should be called to enable or disable
+ * AlternativeNetworkAccess service on the device.
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ *
+ * @param enable enable(True) or disable(False)
+ * @return returns true if successfully set.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setAlternativeNetworkAccessState(boolean enable) {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ boolean ret = false;
+ try {
+ IAnas iAlternativeAccessService = getIAnas();
+ if (iAlternativeAccessService != null) {
+ ret = iAlternativeAccessService.setEnable(enable, pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "enableAlternativeNetworkAccess RemoteException", ex);
+ }
+
+ return ret;
+ }
+
+ /**
+ * is AlternativeNetworkAccessService enabled
+ *
+ * This method should be called to determine if the AlternativeNetworkAccessService is
+ * enabled
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public boolean isAlternativeNetworkAccessEnabled() {
+ String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+ boolean isEnabled = false;
+
+ try {
+ IAnas iAlternativeAccessService = getIAnas();
+ if (iAlternativeAccessService != null) {
+ isEnabled = iAlternativeAccessService.isEnabled(pkgForDebug);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "enableAlternativeNetworkAccess RemoteException", ex);
+ }
+
+ return isEnabled;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IAnas.aidl b/telephony/java/com/android/internal/telephony/IAnas.aidl
new file mode 100755
index 0000000..88d681a
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IAnas.aidl
@@ -0,0 +1,52 @@
+/*
+ * 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.internal.telephony;
+
+
+interface IAnas {
+
+ /**
+ * Enable or disable Alternative Network Access service.
+ *
+ * This method should be called to enable or disable
+ * AlternativeNetworkAccess service on the device.
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param enable enable(True) or disable(False)
+ * @param callingPackage caller's package name
+ * @return returns true if successfully set.
+ */
+ boolean setEnable(boolean enable, String callingPackage);
+
+ /**
+ * is Alternative Network Access service enabled
+ *
+ * This method should be called to determine if the Alternative Network Access service is enabled
+ *
+ * <p>
+ * Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+ *
+ * @param callingPackage caller's package name
+ */
+ boolean isEnabled(String callingPackage);
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a7e5581..c0bccde 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1530,6 +1530,34 @@
void setDataRoamingEnabled(int subId, boolean isEnabled);
/**
+ * Gets the roaming mode for the CDMA phone with the subscription id {@code subId}.
+ *
+ * @param the subscription id.
+ * @return the roaming mode for CDMA phone.
+ */
+ int getCdmaRoamingMode(int subId);
+
+ /**
+ * Sets the roaming mode on the CDMA phone with the subscription {@code subId} to the given
+ * roaming mode {@code mode}.
+ *
+ * @param subId the subscription id.
+ * @param mode the roaming mode should be set.
+ * @return {@code true} if successed.
+ */
+ boolean setCdmaRoamingMode(int subId, int mode);
+
+ /**
+ * Sets the subscription mode for CDMA phone with the subscription {@code subId} to the given
+ * subscription mode {@code mode}.
+ *
+ * @param subId the subscription id.
+ * @param mode the subscription mode should be set.
+ * @return {@code true} if successed.
+ */
+ boolean setCdmaSubscriptionMode(int subId, int mode);
+
+ /**
* A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2,
* plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config
* (also any country or carrier overlays) to be loaded when using a test SIM with a call box.
@@ -1553,4 +1581,9 @@
* @hide
*/
int getNumberOfModemsWithSimultaneousDataConnections(int subId, String callingPackage);
+
+ /**
+ * Return the network selection mode on the subscription with id {@code subId}.
+ */
+ int getNetworkSelectionMode(int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index e0e1a7b..43d56b3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,6 +32,8 @@
interface ITelephonyRegistry {
void addOnSubscriptionsChangedListener(String pkg,
IOnSubscriptionsChangedListener callback);
+ void addOnOpportunisticSubscriptionsChangedListener(String pkg,
+ IOnSubscriptionsChangedListener callback);
void removeOnSubscriptionsChangedListener(String pkg,
IOnSubscriptionsChangedListener callback);
void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow);
@@ -73,6 +75,7 @@
int activationState, int activationType);
void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
+ void notifyOpportunisticSubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
diff --git a/test-mock/api/system-current.txt b/test-mock/api/system-current.txt
index 20401a5..3bd3d68 100644
--- a/test-mock/api/system-current.txt
+++ b/test-mock/api/system-current.txt
@@ -11,6 +11,7 @@
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
+ method public boolean arePermissionsIndividuallyControlled();
method public java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
diff --git a/tests/SystemMemoryTest/README.txt b/tests/SystemMemoryTest/README.txt
index de5042c..8ffca15 100644
--- a/tests/SystemMemoryTest/README.txt
+++ b/tests/SystemMemoryTest/README.txt
@@ -13,8 +13,7 @@
You can manually run the test as follows:
- make tradefed-all system-memory-test SystemMemoryTestDevice
- tradefed.sh run commandAndExit template/local_min --template:map test=system-memory-test
+ atest -v system-memory-test
This installs and runs the test on device. You can see the metrics in the
tradefed output.
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
index 7de092a..616983e 100644
--- a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -66,6 +66,18 @@
}
/**
+ * Writes the given <code>text</code> to a log with the given label.
+ */
+ private void logText(String label, String text) throws IOException {
+ File file = File.createTempFile(label, "txt");
+ PrintStream ps = new PrintStream(file);
+ ps.print(text);
+ try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
+ logs.addTestLog(label, LogDataType.TEXT, dataStream);
+ }
+ }
+
+ /**
* Returns the pid for the process with the given name.
*/
private int getPidForProcess(String name)
@@ -111,13 +123,7 @@
// Read showmap for system server and add it as a test log
String showmap = device.executeShellCommand("showmap " + pid);
- String showmapLabel = label + ".system_server.showmap";
- File file = File.createTempFile(showmapLabel, "txt");
- PrintStream ps = new PrintStream(file);
- ps.print(showmap);
- try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
- logs.addTestLog(showmapLabel, LogDataType.TEXT, dataStream);
- }
+ logText(label + ".system_server.showmap", showmap);
// Extract VSS, PSS and RSS from the showmap and output them as metrics.
// The last lines of the showmap output looks something like:
@@ -140,6 +146,11 @@
throw new MetricsException("unexpected showmap format", e);
}
+ // Run debuggerd -j to get GC stats for system server and add it as a
+ // test log
+ String debuggerd = device.executeShellCommand("debuggerd -j " + pid);
+ logText(label + ".system_server.debuggerd", debuggerd);
+
// TODO: Experiment with other additional metrics.
// TODO: Consider launching an instrumentation to collect metrics from
diff --git a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
index 918873b..d9bb7db 100644
--- a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
+++ b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
@@ -28,6 +28,7 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.stubbing.Stubber;
+import org.mockito.quality.Strictness;
import junit.framework.Assert;
import java.util.Locale;
@@ -210,12 +211,16 @@
}
public static abstract class CountDownBehaviour extends StubberImpl {
+ public CountDownBehaviour(Strictness strictness) {
+ super(strictness);
+ }
+
/** Used to mock methods that return a result. */
public abstract Stubber andReturn(Object result);
}
public static CountDownBehaviour doCountDown(final CountDownLatch latch) {
- return new CountDownBehaviour() {
+ return new CountDownBehaviour(Strictness.WARN) {
@Override
public <T> T when(T mock) {
return Mockito.doAnswer(new Answer<Void>() {
@@ -229,7 +234,7 @@
@Override
public Stubber andReturn(final Object result) {
- return new StubberImpl() {
+ return new StubberImpl(Strictness.WARN) {
@Override
public <T> T when(T mock) {
return Mockito.doAnswer(new Answer<Object>() {
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 4a0931a..6c46e67 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -59,6 +59,8 @@
def read_lines(filename):
"""Reads entire file and return it as a list of lines.
+ Lines which begin with a hash are ignored.
+
Args:
filename (string): Path to the file to read from.
@@ -66,7 +68,7 @@
list: Lines of the loaded file as a list of strings.
"""
with open(filename, 'r') as f:
- return f.readlines()
+ return filter(lambda line: not line.startswith('#'), f.readlines())
def write_lines(filename, lines):
"""Writes list of lines into a file, overwriting the file it it exists.
diff --git a/tools/hiddenapi/sort_api.sh b/tools/hiddenapi/sort_api.sh
index 1c6eb1b..bdcc807 100755
--- a/tools/hiddenapi/sort_api.sh
+++ b/tools/hiddenapi/sort_api.sh
@@ -11,8 +11,14 @@
readarray A < "$source_list"
# Sort
IFS=$'\n'
+# Stash away comments
+C=( $(grep -E '^#' <<< "${A[*]}") )
+A=( $(grep -v -E '^#' <<< "${A[*]}") )
+# Sort entries
A=( $(LC_COLLATE=C sort -f <<< "${A[*]}") )
A=( $(uniq <<< "${A[*]}") )
+# Concatenate comments and entries
+A=( ${C[*]} ${A[*]} )
unset IFS
# Dump array back into the file
printf '%s\n' "${A[@]}" > "$dest_list"
diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp
new file mode 100644
index 0000000..1aca3ed
--- /dev/null
+++ b/tools/processors/unsupportedappusage/Android.bp
@@ -0,0 +1,25 @@
+
+java_library_host {
+ name: "unsupportedappusage-annotation-processor",
+ java_resources: [
+ "META-INF/**/*",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ static_libs: [
+ "guava",
+ "unsupportedappusage-annotation"
+ ],
+ openjdk9: {
+ javacflags: [
+ "--add-modules=jdk.compiler",
+ "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
+ },
+
+ use_tools_jar: true,
+}
diff --git a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..4a969d3
--- /dev/null
+++ b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+android.processor.unsupportedappusage.UnsupportedAppUsageProcessor
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
new file mode 100644
index 0000000..ef29146
--- /dev/null
+++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
@@ -0,0 +1,228 @@
+/*
+ * 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 android.processor.unsupportedappusage;
+
+import static javax.lang.model.element.ElementKind.PACKAGE;
+import static javax.tools.Diagnostic.Kind.ERROR;
+import static javax.tools.Diagnostic.Kind.WARNING;
+
+import android.annotation.UnsupportedAppUsage;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.sun.tools.javac.code.Type;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Builds a dex signature for a given method or field.
+ */
+public class SignatureBuilder {
+
+ private static final Map<TypeKind, String> TYPE_MAP = ImmutableMap.<TypeKind, String>builder()
+ .put(TypeKind.BOOLEAN, "Z")
+ .put(TypeKind.BYTE, "B")
+ .put(TypeKind.CHAR, "C")
+ .put(TypeKind.DOUBLE, "D")
+ .put(TypeKind.FLOAT, "F")
+ .put(TypeKind.INT, "I")
+ .put(TypeKind.LONG, "J")
+ .put(TypeKind.SHORT, "S")
+ .put(TypeKind.VOID, "V")
+ .build();
+
+ private final Messager mMessager;
+
+ /**
+ * Exception used internally when we can't build a signature. Whenever this is thrown, an error
+ * will also be written to the Messager.
+ */
+ private class SignatureBuilderException extends Exception {
+ public SignatureBuilderException(String message) {
+ super(message);
+ }
+ public void report(Element offendingElement) {
+ mMessager.printMessage(ERROR, getMessage(), offendingElement);
+ }
+ }
+
+ public SignatureBuilder(Messager messager) {
+ mMessager = messager;
+ }
+
+ /**
+ * Returns a list of enclosing elements for the given element, with the package first, and
+ * excluding the element itself.
+ */
+ private List<Element> getEnclosingElements(Element e) {
+ List<Element> enclosing = new ArrayList<>();
+ e = e.getEnclosingElement(); // don't include the element itself.
+ while (e != null) {
+ enclosing.add(e);
+ e = e.getEnclosingElement();
+ }
+ Collections.reverse(enclosing);
+ return enclosing;
+ }
+
+ /**
+ * Get the dex signature for a clazz, in format "Lpackage/name/Outer$Inner;"
+ */
+ private String getClassSignature(TypeElement clazz) {
+ StringBuilder sb = new StringBuilder("L");
+ for (Element enclosing : getEnclosingElements(clazz)) {
+ if (enclosing.getKind() == PACKAGE) {
+ sb.append(((PackageElement) enclosing)
+ .getQualifiedName()
+ .toString()
+ .replace('.', '/'));
+ sb.append('/');
+ } else {
+ sb.append(enclosing.getSimpleName()).append('$');
+ }
+
+ }
+ return sb
+ .append(clazz.getSimpleName())
+ .append(";")
+ .toString();
+ }
+
+ /**
+ * Returns the type signature for a given type. For primitive types, a single character.
+ * For classes, the class signature. For arrays, a "[" preceeding the component type.
+ */
+ private String getTypeSignature(TypeMirror type) throws SignatureBuilderException {
+ String sig = TYPE_MAP.get(type.getKind());
+ if (sig != null) {
+ return sig;
+ }
+ switch (type.getKind()) {
+ case ARRAY:
+ return "[" + getTypeSignature(((ArrayType) type).getComponentType());
+ case DECLARED:
+ Element declaring = ((DeclaredType) type).asElement();
+ if (!(declaring instanceof TypeElement)) {
+ throw new SignatureBuilderException(
+ "Can't handle declared type of kind " + declaring.getKind());
+ }
+ return getClassSignature((TypeElement) declaring);
+ case TYPEVAR:
+ Type.TypeVar typeVar = (Type.TypeVar) type;
+ if (typeVar.getLowerBound().getKind() != TypeKind.NULL) {
+ return getTypeSignature(typeVar.getLowerBound());
+ } else if (typeVar.getUpperBound().getKind() != TypeKind.NULL) {
+ return getTypeSignature(typeVar.getUpperBound());
+ } else {
+ throw new SignatureBuilderException("Can't handle typevar with no bound");
+ }
+
+ default:
+ throw new SignatureBuilderException("Can't handle type of kind " + type.getKind());
+ }
+ }
+
+ /**
+ * Get the signature for an executable, either a method or a constructor.
+ *
+ * @param name "<init>" for constructor, else the method name
+ * @param method The executable element in question.
+ */
+ private String getExecutableSignature(CharSequence name, ExecutableElement method)
+ throws SignatureBuilderException {
+ StringBuilder sig = new StringBuilder();
+ sig.append(getClassSignature((TypeElement) method.getEnclosingElement()))
+ .append("->")
+ .append(name)
+ .append("(");
+ for (VariableElement param : method.getParameters()) {
+ sig.append(getTypeSignature(param.asType()));
+ }
+ sig.append(")")
+ .append(getTypeSignature(method.getReturnType()));
+ return sig.toString();
+ }
+
+ private String buildMethodSignature(ExecutableElement method) throws SignatureBuilderException {
+ return getExecutableSignature(method.getSimpleName(), method);
+ }
+
+ private String buildConstructorSignature(ExecutableElement cons)
+ throws SignatureBuilderException {
+ return getExecutableSignature("<init>", cons);
+ }
+
+ private String buildFieldSignature(VariableElement field) throws SignatureBuilderException {
+ StringBuilder sig = new StringBuilder();
+ sig.append(getClassSignature((TypeElement) field.getEnclosingElement()))
+ .append("->")
+ .append(field.getSimpleName())
+ .append(":")
+ .append(getTypeSignature(field.asType()))
+ ;
+ return sig.toString();
+ }
+
+ public String buildSignature(Element element) {
+ UnsupportedAppUsage uba = element.getAnnotation(UnsupportedAppUsage.class);
+ try {
+ String signature;
+ switch (element.getKind()) {
+ case METHOD:
+ signature = buildMethodSignature((ExecutableElement) element);
+ break;
+ case CONSTRUCTOR:
+ signature = buildConstructorSignature((ExecutableElement) element);
+ break;
+ case FIELD:
+ signature = buildFieldSignature((VariableElement) element);
+ break;
+ default:
+ return null;
+ }
+ // if we have an expected signature on the annotation, warn if it doesn't match.
+ if (!Strings.isNullOrEmpty(uba.expectedSignature())) {
+ if (!signature.equals(uba.expectedSignature())) {
+ mMessager.printMessage(
+ WARNING,
+ String.format("Expected signature doesn't match generated signature.\n"
+ + " Expected: %s\n Generated: %s",
+ uba.expectedSignature(), signature),
+ element);
+ }
+ }
+ return signature;
+ } catch (SignatureBuilderException problem) {
+ problem.report(element);
+ return null;
+ }
+ }
+}
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
new file mode 100644
index 0000000..1d4c435
--- /dev/null
+++ b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
@@ -0,0 +1,173 @@
+/*
+ * 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 android.processor.unsupportedappusage;
+
+import static javax.tools.StandardLocation.CLASS_OUTPUT;
+
+import android.annotation.UnsupportedAppUsage;
+
+import com.google.common.base.Joiner;
+import com.sun.tools.javac.model.JavacElements;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.util.Pair;
+import com.sun.tools.javac.util.Position;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Stream;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/**
+ * Annotation processor for {@link UnsupportedAppUsage} annotations.
+ *
+ * This processor currently outputs two things:
+ * 1. A greylist.txt containing dex signatures of all annotated elements.
+ * 2. A CSV file with a mapping of dex signatures to corresponding source positions.
+ *
+ * The first will be used at a later stage of the build to add access flags to the dex file. The
+ * second is used for automating updates to the annotations themselves.
+ */
+@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage"})
+public class UnsupportedAppUsageProcessor extends AbstractProcessor {
+
+ // Package name for writing output. Output will be written to the "class output" location within
+ // this package.
+ private static final String PACKAGE = "unsupportedappusage";
+ private static final String INDEX_CSV = "unsupportedappusage_index.csv";
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latest();
+ }
+
+ /**
+ * Write the contents of a stream to a text file, with one line per item.
+ */
+ private void writeToFile(String name,
+ String headerLine,
+ Stream<?> contents) throws IOException {
+ PrintStream out = new PrintStream(processingEnv.getFiler().createResource(
+ CLASS_OUTPUT,
+ PACKAGE,
+ name)
+ .openOutputStream());
+ out.println(headerLine);
+ contents.forEach(o -> out.println(o));
+ if (out.checkError()) {
+ throw new IOException("Error when writing to " + name);
+ }
+ out.close();
+ }
+
+ /**
+ * Find the annotation mirror for the @UnsupportedAppUsage annotation on the given element.
+ */
+ private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) {
+ for (AnnotationMirror m : e.getAnnotationMirrors()) {
+ TypeElement type = (TypeElement) m.getAnnotationType().asElement();
+ if (type.getQualifiedName().toString().equals(
+ UnsupportedAppUsage.class.getCanonicalName())) {
+ return m;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a CSV header line for the columns returned by
+ * {@link #getAnnotationIndex(String, Element)}.
+ */
+ private String getCsvHeaders() {
+ return Joiner.on(',').join(
+ "signature",
+ "file",
+ "startline",
+ "startcol",
+ "endline",
+ "endcol"
+ );
+ }
+
+ /**
+ * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation
+ * attached to it. It returns CSV in the format:
+ * dex-signature,filename,start-line,start-col,end-line,end-col
+ *
+ * The positions refer to the annotation itself, *not* the annotated member. This can therefore
+ * be used to read just the annotation from the file, and to perform in-place edits on it.
+ *
+ * @param signature the dex signature for the element.
+ * @param annotatedElement The annotated element
+ * @return A single line of CSV text
+ */
+ private String getAnnotationIndex(String signature, Element annotatedElement) {
+ JavacElements javacElem = (JavacElements) processingEnv.getElementUtils();
+ AnnotationMirror unsupportedAppUsage =
+ getUnsupportedAppUsageAnnotationMirror(annotatedElement);
+ Pair<JCTree, JCTree.JCCompilationUnit> pair =
+ javacElem.getTreeAndTopLevel(annotatedElement, unsupportedAppUsage, null);
+ Position.LineMap lines = pair.snd.lineMap;
+ return Joiner.on(",").join(
+ signature,
+ pair.snd.getSourceFile().getName(),
+ lines.getLineNumber(pair.fst.pos().getStartPosition()),
+ lines.getColumnNumber(pair.fst.pos().getStartPosition()),
+ lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
+ lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)));
+ }
+
+ /**
+ * This is the main entry point in the processor, called by the compiler.
+ */
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith(
+ UnsupportedAppUsage.class);
+ if (annotated.size() == 0) {
+ return true;
+ }
+ // build signatures for each annotated member, and put them in a map of signature to member
+ Map<String, Element> signatureMap = new TreeMap<>();
+ SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager());
+ for (Element e : annotated) {
+ String sig = sb.buildSignature(e);
+ if (sig != null) {
+ signatureMap.put(sig, e);
+ }
+ }
+ try {
+ writeToFile(INDEX_CSV,
+ getCsvHeaders(),
+ signatureMap.entrySet()
+ .stream()
+ .map(e -> getAnnotationIndex(e.getKey() ,e.getValue())));
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to write output", e);
+ }
+ return true;
+ }
+}
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 40ee490..f294728 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -184,16 +184,6 @@
expectedNumber++;
}
- // Skips the key value pair atom.
- for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
- it != fields.end(); it++) {
- const FieldDescriptor *field = it->second;
- java_type_t javaType = java_type(field);
- if (javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
- return 0;
- }
- }
-
// Check that only allowed types are present. Remove any invalid ones.
for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
it != fields.end(); it++) {
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 8038a3a..83a6631 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -235,6 +235,10 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", const std::map<int, int64_t>& arg%d_1, "
+ "const std::map<int, char const*>& arg%d_2, "
+ "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -277,6 +281,30 @@
fprintf(out, " event.end();\n");
fprintf(out, " }\n");
fprintf(out, " event.end();\n\n");
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, " event.begin();\n\n");
+ fprintf(out, " for (const auto& it : arg%d_1) {\n", argIndex);
+ fprintf(out, " event.begin();\n");
+ fprintf(out, " event << it.first;\n");
+ fprintf(out, " event << it.second;\n");
+ fprintf(out, " event.end();\n");
+ fprintf(out, " }\n");
+
+ fprintf(out, " for (const auto& it : arg%d_2) {\n", argIndex);
+ fprintf(out, " event.begin();\n");
+ fprintf(out, " event << it.first;\n");
+ fprintf(out, " event << it.second;\n");
+ fprintf(out, " event.end();\n");
+ fprintf(out, " }\n");
+
+ fprintf(out, " for (const auto& it : arg%d_3) {\n", argIndex);
+ fprintf(out, " event.begin();\n");
+ fprintf(out, " event << it.first;\n");
+ fprintf(out, " event << it.second;\n");
+ fprintf(out, " event.end();\n");
+ fprintf(out, " }\n");
+
+ fprintf(out, " event.end();\n\n");
} else {
if (*arg == JAVA_TYPE_STRING) {
fprintf(out, " if (arg%d == NULL) {\n", argIndex);
@@ -300,7 +328,7 @@
signature != atoms.signatures.end(); signature++) {
int argIndex;
- fprintf(out, "int \n");
+ fprintf(out, "int\n");
fprintf(out, "stats_write(int32_t code");
argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -317,6 +345,10 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", const std::map<int, int64_t>& arg%d_1, "
+ "const std::map<int, char const*>& arg%d_2, "
+ "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -343,6 +375,8 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", arg%d_1, arg%d_2, arg%d_3", argIndex, argIndex, argIndex);
} else {
fprintf(out, ", arg%d", argIndex);
}
@@ -496,6 +530,11 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", const std::map<int, int64_t>& %s_int"
+ ", const std::map<int, char const*>& %s_str"
+ ", const std::map<int, float>& %s_float",
+ field->name.c_str(), field->name.c_str(), field->name.c_str());
} else {
fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
}
@@ -508,7 +547,7 @@
const AtomDecl &attributionDecl) {
for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
signature != signatures.end(); signature++) {
- fprintf(out, "int %s(int32_t code ", method_name.c_str());
+ fprintf(out, "int %s(int32_t code", method_name.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
@@ -523,6 +562,10 @@
chainField.name.c_str(), chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", const std::map<int, int64_t>& arg%d_1, "
+ "const std::map<int, char const*>& arg%d_2, "
+ "const std::map<int, float>& arg%d_3", argIndex, argIndex, argIndex);
} else {
fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
}
@@ -637,6 +680,8 @@
field != atom.fields.end(); field++) {
if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
fprintf(out, ", android.os.WorkSource workSource");
+ } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", SparseArray<Object> value_map");
} else {
fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
}
@@ -658,6 +703,8 @@
fprintf(out, ", %s[] %s",
java_type_name(chainField.javaType), chainField.name.c_str());
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", SparseArray<Object> value_map");
} else {
fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
}
@@ -746,6 +793,7 @@
fprintf(out, "package android.util;\n");
fprintf(out, "\n");
fprintf(out, "import android.os.WorkSource;\n");
+ fprintf(out, "import android.util.SparseArray;\n");
fprintf(out, "import java.util.ArrayList;\n");
fprintf(out, "\n");
fprintf(out, "\n");
@@ -837,6 +885,8 @@
switch (type) {
case JAVA_TYPE_INT:
return "jintArray";
+ case JAVA_TYPE_FLOAT:
+ return "jfloatArray";
case JAVA_TYPE_STRING:
return "jobjectArray";
default:
@@ -873,6 +923,9 @@
case JAVA_TYPE_ATTRIBUTION_CHAIN:
result += "_AttributionChain";
break;
+ case JAVA_TYPE_KEY_VALUE_PAIR:
+ result += "_KeyValuePairs";
+ break;
default:
result += "_UNKNOWN";
break;
@@ -914,6 +967,8 @@
result += "[";
result += java_type_signature(chainField.javaType);
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ result += "Landroid/util/SparseArray;";
} else {
result += java_type_signature(*arg);
}
@@ -922,6 +977,43 @@
return result;
}
+static void write_key_value_map_jni(FILE* out) {
+ fprintf(out, " std::map<int, int64_t> int64_t_map;\n");
+ fprintf(out, " std::map<int, float> float_map;\n");
+ fprintf(out, " std::map<int, char const*> string_map;\n\n");
+
+ fprintf(out, " jclass jmap_class = env->FindClass(\"android/util/SparseArray\");\n");
+
+ fprintf(out, " jmethodID jget_size_method = env->GetMethodID(jmap_class, \"size\", \"()I\");\n");
+ fprintf(out, " jmethodID jget_key_method = env->GetMethodID(jmap_class, \"keyAt\", \"(I)I\");\n");
+ fprintf(out, " jmethodID jget_value_method = env->GetMethodID(jmap_class, \"valueAt\", \"(I)Ljava/lang/Object;\");\n\n");
+
+
+ fprintf(out, " std::vector<std::unique_ptr<ScopedUtfChars>> scoped_ufs;\n\n");
+
+ fprintf(out, " jclass jlong_class = env->FindClass(\"java/lang/Long\");\n");
+ fprintf(out, " jclass jfloat_class = env->FindClass(\"java/lang/Float\");\n");
+ fprintf(out, " jclass jstring_class = env->FindClass(\"java/lang/String\");\n");
+ fprintf(out, " jmethodID jget_long_method = env->GetMethodID(jlong_class, \"longValue\", \"()J\");\n");
+ fprintf(out, " jmethodID jget_float_method = env->GetMethodID(jfloat_class, \"floatValue\", \"()F\");\n\n");
+
+ fprintf(out, " jint jsize = env->CallIntMethod(value_map, jget_size_method);\n");
+ fprintf(out, " for(int i = 0; i < jsize; i++) {\n");
+ fprintf(out, " jint key = env->CallIntMethod(value_map, jget_key_method, i);\n");
+ fprintf(out, " jobject jvalue_obj = env->CallObjectMethod(value_map, jget_value_method, i);\n");
+ fprintf(out, " if (jvalue_obj == NULL) { continue; }\n");
+ fprintf(out, " if (env->IsInstanceOf(jvalue_obj, jlong_class)) {\n");
+ fprintf(out, " int64_t_map[key] = env->CallLongMethod(jvalue_obj, jget_long_method);\n");
+ fprintf(out, " } else if (env->IsInstanceOf(jvalue_obj, jfloat_class)) {\n");
+ fprintf(out, " float_map[key] = env->CallFloatMethod(jvalue_obj, jget_float_method);\n");
+ fprintf(out, " } else if (env->IsInstanceOf(jvalue_obj, jstring_class)) {\n");
+ fprintf(out, " std::unique_ptr<ScopedUtfChars> utf(new ScopedUtfChars(env, (jstring)jvalue_obj));\n");
+ fprintf(out, " if (utf->c_str() != NULL) { string_map[key] = utf->c_str(); }\n");
+ fprintf(out, " scoped_ufs.push_back(std::move(utf));\n");
+ fprintf(out, " }\n");
+ fprintf(out, " }\n");
+}
+
static int
write_stats_log_jni(FILE* out, const string& java_method_name, const string& cpp_method_name,
const set<vector<java_type_t>>& signatures, const AtomDecl &attributionDecl)
@@ -942,6 +1034,8 @@
fprintf(out, ", %s %s", jni_array_type_name(chainField.javaType),
chainField.name.c_str());
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", jobject value_map");
} else {
fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
}
@@ -954,6 +1048,7 @@
// Prepare strings
argIndex = 1;
bool hadStringOrChain = false;
+ bool isKeyValuePairAtom = false;
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_STRING) {
@@ -1006,18 +1101,23 @@
}
fprintf(out, "\n");
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ isKeyValuePairAtom = true;
}
argIndex++;
}
// Emit this to quiet the unused parameter warning if there were no strings or attribution
// chains.
- if (!hadStringOrChain) {
+ if (!hadStringOrChain && !isKeyValuePairAtom) {
fprintf(out, " (void)env;\n");
}
+ if (isKeyValuePairAtom) {
+ write_key_value_map_jni(out);
+ }
// stats_write call
argIndex = 1;
- fprintf(out, " int ret = android::util::%s(code", cpp_method_name.c_str());
+ fprintf(out, "\n int ret = android::util::%s(code", cpp_method_name.c_str());
for (vector<java_type_t>::const_iterator arg = signature->begin();
arg != signature->end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -1030,6 +1130,8 @@
fprintf(out, ", %s_vec", chainField.name.c_str());
}
}
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", int64_t_map, string_map, float_map");
} else {
const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
@@ -1063,6 +1165,7 @@
}
argIndex++;
}
+
fprintf(out, " return ret;\n");
fprintf(out, "}\n");
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 6fb278c..819e75b 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -15,6 +15,7 @@
],
static_libs: [
+ "libbase",
"libinput",
"libutils",
"libcutils",
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index bbfcba6..f31f771 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -18,7 +18,6 @@
#include <input/KeyLayoutMap.h>
#include <input/VirtualKeyMap.h>
#include <utils/PropertyMap.h>
-#include <utils/String8.h>
#include <stdarg.h>
#include <stdio.h>
@@ -98,7 +97,7 @@
case FILETYPE_KEYLAYOUT: {
sp<KeyLayoutMap> map;
- status_t status = KeyLayoutMap::load(String8(filename), &map);
+ status_t status = KeyLayoutMap::load(filename, &map);
if (status) {
error("Error %d parsing key layout file.\n\n", status);
return false;
@@ -108,7 +107,7 @@
case FILETYPE_KEYCHARACTERMAP: {
sp<KeyCharacterMap> map;
- status_t status = KeyCharacterMap::load(String8(filename),
+ status_t status = KeyCharacterMap::load(filename,
KeyCharacterMap::FORMAT_ANY, &map);
if (status) {
error("Error %d parsing key character map file.\n\n", status);
@@ -130,7 +129,7 @@
case FILETYPE_VIRTUALKEYDEFINITION: {
VirtualKeyMap* map;
- status_t status = VirtualKeyMap::load(String8(filename), &map);
+ status_t status = VirtualKeyMap::load(filename, &map);
if (status) {
error("Error %d parsing virtual key definition file.\n\n", status);
return false;
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 7c99c49..1426383 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -48,7 +48,7 @@
private MessageDigest mDigester;
public WifiAwareAgentNetworkSpecifier() {
- // do nothing, already initialized to empty
+ initialize();
}
public WifiAwareAgentNetworkSpecifier(WifiAwareNetworkSpecifier ns) {
diff --git a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
index ef9c59f..bb02dec 100644
--- a/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
+++ b/wifi/java/android/net/wifi/hotspot2/ProvisioningCallback.java
@@ -85,15 +85,21 @@
public static final int OSU_FAILURE_SOAP_MESSAGE_EXCHANGE = 11;
/**
- * The reason code for provisioning failure when a redirect server fails to start.
+ * The reason code for provisioning failure when a redirect listener fails to start.
*/
- public static final int OSU_FAILURE_START_REDIRECT_SERVER = 12;
+ public static final int OSU_FAILURE_START_REDIRECT_LISTENER = 12;
+
+ /**
+ * The reason code for provisioning failure when a redirect listener timed out to receive a HTTP
+ * redirect response.
+ */
+ public static final int OSU_FAILURE_TIMED_OUT_REDIRECT_LISTENER = 13;
/**
* The reason code for provisioning failure when there is no OSU activity to listen to
* {@link WifiManager#ACTION_PASSPOINT_LAUNCH_OSU_VIEW} intent.
*/
- public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 13;
+ public static final int OSU_FAILURE_NO_OSU_ACTIVITY_FOUND = 14;
/**
* The status code for provisioning flow to indicate connecting to OSU AP
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 3517984..3b9f93e 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -18,14 +18,14 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
-import android.os.Parcel;
import android.net.MacAddress;
import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
-import android.net.wifi.WifiInfo;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
@@ -33,6 +33,7 @@
/**
* Unit tests for {@link android.net.wifi.WifiConfiguration}.
*/
+@SmallTest
public class WifiConfigurationTest {
@Before
diff --git a/wifi/tests/src/android/net/wifi/WifiSsidTest.java b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
index e5794c5..b58f2c7 100644
--- a/wifi/tests/src/android/net/wifi/WifiSsidTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiSsidTest.java
@@ -19,14 +19,16 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.support.test.filters.SmallTest;
+
import org.junit.Test;
import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
/**
* Unit tests for {@link android.net.wifi.WifiSsid}.
*/
+@SmallTest
public class WifiSsidTest {
private static final String TEST_SSID = "Test SSID";
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index 0515e06..657e5a7 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -18,6 +18,7 @@
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
@@ -60,6 +61,13 @@
WifiAwareAgentNetworkSpecifier.CREATOR.createFromParcel(parcelR);
assertEquals(dut, rereadDut);
+
+ // Ensure that individual network specifiers are satisfied by both the original & marshaled
+ // |WifiAwareNetworkAgentSpecifier instances.
+ for (WifiAwareNetworkSpecifier ns : nsSet) {
+ assertTrue(dut.satisfiesAwareNetworkSpecifier(ns));
+ assertTrue(rereadDut.satisfiesAwareNetworkSpecifier(ns));
+ }
}
/**
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index e492475..80f00a4 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -19,11 +19,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.support.test.filters.SmallTest;
+
import org.junit.Test;
/**
* Unit test harness for {@link android.net.wifi.p2p.WifiP2pDevice}
*/
+@SmallTest
public class WifiP2pDeviceTest {
/**
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
index e8e4dc2..2132b41 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pManagerTest.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
import libcore.junit.util.ResourceLeakageDetector;
@@ -33,6 +34,7 @@
/**
* Unit test harness for WifiP2pManager.
*/
+@SmallTest
public class WifiP2pManagerTest {
private WifiP2pManager mDut;
private TestLooper mTestLooper;
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index ccb9031..8997ae9 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -31,6 +31,7 @@
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
@@ -45,6 +46,7 @@
/**
* Unit test harness for WifiRttManager class.
*/
+@SmallTest
public class WifiRttManagerTest {
private WifiRttManager mDut;
private TestLooper mMockLooper;